summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2001-04-02 09:02:23 +0000
committerJim Meyering <jim@meyering.net>2001-04-02 09:02:23 +0000
commit46e9ed6beb3adf84c1a023af9082c0508ad07b1d (patch)
treef2f0cc2b475d37b3f6f6bf04facf522076913d25 /src
parent3c0e576c6d920c2a5e8626bfe3c45b9c14ae0ab7 (diff)
downloadcoreutils-46e9ed6beb3adf84c1a023af9082c0508ad07b1d.tar.xz
Include <getopt.h>.
(usage, main): Add support for long options, and check option syntax as POSIX requires, though (as usual for GNU apps) options can follow file names unless POSIXLY_CORRECT is set. Many diagnostic revamped. (long_options): New constant. (badfieldspec): New arg MSGID. Mark as noreturn. (parse_field_count): New arg MSGID; if null, just return null on error. (new_key): Renamed from key_init. All callers changed. Now allocates the new key.
Diffstat (limited to 'src')
-rw-r--r--src/sort.c531
1 files changed, 267 insertions, 264 deletions
diff --git a/src/sort.c b/src/sort.c
index ea29e68de..5d1a80a74 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -23,6 +23,7 @@
#include <config.h>
+#include <getopt.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
@@ -291,29 +292,36 @@ Usage: %s [OPTION]... [FILE]...\n\
printf (_("\
Write sorted concatenation of all FILE(s) to standard output.\n\
\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 blanks and alphanumeric 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 printable characters in keys\n\
- -k POS1[,POS2] start a key at POS1, end it *at* POS2 (origin 1)\n\
- -m merge already sorted files, do not sort\n\
- -M compare (unknown) < `JAN' < ... < `DEC', imply -b\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\
- -S SIZE use SIZE for main memory sorting\n\
- -t SEP use SEParator instead of non- to whitespace transition\n\
- -T DIRECTORY use DIRECTORY for temporary files, not $TMPDIR or %s\n\
- multiple -T options specify multiple directories\n\
- -u with -c, check for strict ordering;\n\
- with -m, only output the first of an equal sequence\n\
- -z end lines with 0 byte, not newline, for find -print0\n\
- +POS1 [-POS2] start a key at POS1, end it *before* POS2 (origin 0)\n\
- Warning: this option is obsolescent and support for it\n\
- will be withdrawn. Use -k instead.\n\
+Ordering options:\n\
+\n\
+ -b, --ignore-leading-blanks ignore leading blanks\n\
+ -d, --dictionary-order consider only blanks and alphanumeric characters\n\
+ -f, --ignore-case fold lower case to upper case characters\n\
+ -g, --general-numeric-sort compare according to general numerical value\n\
+ -i, --ignore-nonprinting consider only printable characters\n\
+ -M, --month-sort compare (unknown) < `JAN' < ... < `DEC'\n\
+ -n, --numeric-sort compare according to string numerical value\n\
+ -r, --reverse reverse the result of comparisons\n\
+\n\
+")
+ );
+ printf (_("\
+Other options:\n\
+\n\
+ -c, --check check whether input is sorted; do not sort\n\
+ -k, --key=POS1[,POS2] start a key at POS1, end it at POS 2 (origin 1)\n\
+ -m, --merge merge already sorted files; do not sort\n\
+ -o, --output=FILE write result to FILE instead of standard output\n\
+ -s, --stable stabilize sort by disabling last-resort comparison\n\
+ -S, --buffer-size=SIZE use SIZE for main memory buffer\n\
+ -t, --field-separator=SEP use SEP instead of non- to whitespace transition\n\
+ -T, --temporary-directory=DIR use DIR for temporaries, not $TMPDIR or %s\n\
+ multiple options specify multiple directories\n\
+ -u, --unique with -c: check for strict ordering\n\
+ otherwise: output only the first of an equal run\n\
+ -z, --zero-terminated end lines with 0 byte, not newline\n\
+ +POS1 [-POS2] start a key at POS1, end it before POS2 (origin 0)\n\
+ Warning: this option is obsolescent\n\
--help display this help and exit\n\
--version output version information and exit\n\
\n\
@@ -322,9 +330,9 @@ Write sorted concatenation of all FILE(s) to standard output.\n\
printf (_("\
POS is F[.C][OPTS], where F is the field number and C the character position\n\
in the field, both counted from one with -k, from zero with the obsolescent\n\
-form. OPTS is made up of one or more of Mbdfinr; this effectively disables\n\
-global -Mbdfinr settings for that key. If no key is given, use the entire\n\
-line as the key.\n\
+form. OPTS is made up of one or more single-letter ordering options, which\n\
+override global ordering options for that key. If no key is given, use the\n\
+entire line as the key.\n\
\n\
SIZE may be followed by the following multiplicative suffixes:\n\
%% 1%% of memory, b 1, k 1024 (default), and so on for M, G, T, P, E, Z, Y.\n\
@@ -345,6 +353,31 @@ Set LC_ALL=C to get the traditional sort order that uses native byte values.\n\
exit (status);
}
+static struct option const long_options[] =
+{
+ {"ignore-leading-blanks", no_argument, NULL, 'b'},
+ {"check", no_argument, NULL, 'c'},
+ {"dictionary-order", no_argument, NULL, 'd'},
+ {"ignore-case", no_argument, NULL, 'f'},
+ {"general-numeric-sort", no_argument, NULL, 'g'},
+ {"ignore-nonprinting", no_argument, NULL, 'i'},
+ {"key", required_argument, NULL, 'k'},
+ {"merge", no_argument, NULL, 'm'},
+ {"month-sort", no_argument, NULL, 'M'},
+ {"numeric-sort", no_argument, NULL, 'n'},
+ {"output", required_argument, NULL, 'o'},
+ {"reverse", no_argument, NULL, 'r'},
+ {"stable", no_argument, NULL, 's'},
+ {"buffer-size", required_argument, NULL, 'S'},
+ {"field-separator", required_argument, NULL, 't'},
+ {"temporary-directory", required_argument, NULL, 'T'},
+ {"unique", no_argument, NULL, 'u'},
+ {"zero-terminated", no_argument, NULL, 'z'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {0, 0, 0, 0},
+};
+
/* The set of signals that are caught. */
static sigset_t caught_signals;
@@ -1975,21 +2008,26 @@ insertkey (struct keyfield *key)
key->next = NULL;
}
+/* Report a bad field specification SPEC, with extra info MSGID. */
+
+static void badfieldspec PARAMS ((char const *, char const *))
+ ATTRIBUTE_NORETURN;
static void
-badfieldspec (const char *s)
+badfieldspec (char const *spec, char const *msgid)
{
- error (SORT_FAILURE, 0, _("invalid field specification `%s'"), s);
+ error (SORT_FAILURE, 0, _("%s: invalid field specification `%s'"),
+ _(msgid), spec);
+ abort ();
}
/* Parse the leading integer in STRING and store the resulting value
(which must fit into size_t) into *VAL. Return the address of the
- suffix after the integer. */
+ suffix after the integer. If MSGID is NULL, return NULL after
+ failure; otherwise, report MSGID and exit on failure. */
+
static char const *
-parse_field_count (char const *string, size_t *val)
+parse_field_count (char const *string, size_t *val, char const *msgid)
{
- /* '@' can't possibly be a valid suffix; return &invalid_suffix so that
- the caller will eventually invoke badfieldspec. */
- static char const invalid_suffix = '@';
char *suffix;
uintmax_t n;
@@ -2002,13 +2040,16 @@ parse_field_count (char const *string, size_t *val)
break;
/* Fall through. */
case LONGINT_OVERFLOW:
- error (0, 0, _("count `%.*s' too large"),
- (int) (suffix - string), string);
- return &invalid_suffix;
+ if (msgid)
+ error (SORT_FAILURE, 0, _("%s: count `%.*s' too large"),
+ _(msgid), (int) (suffix - string), string);
+ return NULL;
case LONGINT_INVALID:
- error (0, 0, _("invalid count at start of `%s'"), string);
- return &invalid_suffix;
+ if (msgid)
+ error (SORT_FAILURE, 0, _("%s: invalid count at start of `%s'"),
+ _(msgid), string);
+ return NULL;
}
return suffix;
@@ -2089,20 +2130,24 @@ set_ordering (register const char *s, struct keyfield *key,
return (char *) s;
}
-static void
-key_init (struct keyfield *key)
+static struct keyfield *
+new_key (void)
{
- memset (key, 0, sizeof (*key));
+ struct keyfield *key = (struct keyfield *) xcalloc (1, sizeof *key);
key->eword = -1;
+ return key;
}
int
main (int argc, char **argv)
{
- struct keyfield *key = NULL, gkey;
+ struct keyfield *key;
+ struct keyfield gkey;
char const *s;
int i;
+ int c = 0;
int checkonly = 0, mergeonly = 0, nfiles = 0;
+ int posix_pedantic = (getenv ("POSIXLY_CORRECT") != NULL);
char *minus = "-", **files;
char const *outfile = minus;
static int const sigs[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM };
@@ -2143,9 +2188,6 @@ main (int argc, char **argv)
#endif /* NLS */
- parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
- AUTHORS, usage);
-
have_read_stdin = 0;
inittables ();
@@ -2183,237 +2225,199 @@ main (int argc, char **argv)
files = (char **) xmalloc (sizeof (char *) * argc);
- for (i = 1; i < argc; ++i)
+ for (;;)
{
- if (argv[i][0] == '+')
+ /* Parse an operand as a file after "--" was seen; or if
+ pedantic and a file was seen, unless -c was not seen and the
+ operand is "-o FILE" or "-oFILE". POSIX 1003.1-200x d5
+ removes the exception for the -o options; when it becomes
+ official the code will need to be changed. */
+
+ if (c == -1
+ || (posix_pedantic && nfiles != 0
+ && ! (! checkonly
+ && optind != argc
+ && argv[optind][0] == '-' && argv[optind][1] == 'o'
+ && (argv[optind][2] || optind + 1 != argc)))
+ || ((c = getopt_long (argc, argv,
+ "-bcdfgik:mMno:rsS:t:T:uy::z",
+ long_options, NULL))
+ == -1))
+ {
+ if (optind == argc)
+ break;
+ files[nfiles++] = argv[optind++];
+ }
+ else switch (c)
{
- if (key)
- insertkey (key);
- key = (struct keyfield *) xmalloc (sizeof (struct keyfield));
- key_init (key);
- s = argv[i] + 1;
- if (! (ISDIGIT (*s) || (*s == '.' && ISDIGIT (s[1]))))
- badfieldspec (argv[i]);
- s = parse_field_count (s, &key->sword);
+ case 1:
+ /* Treat +POS1 [-POS2] as a key if possible; but silently
+ treat an operand as a file if it is not a valid +POS1.
+ POSIX 1003.1-200x d5 does not allow support for +POS1, so
+ when it becomes official this code will need to be
+ changed. */
+ key = NULL;
+ if (optarg[0] == '+')
+ {
+ key = new_key ();
+ s = parse_field_count (optarg + 1, &key->sword, NULL);
+ if (s && *s == '.')
+ s = parse_field_count (s + 1, &key->schar, NULL);
+ if (! (key->sword | key->schar))
+ key->sword = -1;
+ if (! s || *set_ordering (s, key, bl_start))
+ {
+ free (key);
+ key = NULL;
+ }
+ else
+ {
+ if (optind != argc && argv[optind][0] == '-'
+ && ISDIGIT (argv[optind][1]))
+ {
+ char const *optarg1 = argv[optind++];
+ s = parse_field_count (optarg1 + 1, &key->eword,
+ N_("invalid number after `-'"));
+ if (*s == '.')
+ s = parse_field_count (s + 1, &key->echar,
+ N_("invalid number after `.'"));
+ if (*set_ordering (s, key, bl_end))
+ badfieldspec (optarg1,
+ N_("stray character in field spec"));
+ }
+ insertkey (key);
+ }
+ }
+ if (! key)
+ files[nfiles++] = optarg;
+ break;
+
+ case 'b':
+ case 'd':
+ case 'f':
+ case 'g':
+ case 'i':
+ case 'M':
+ case 'n':
+ case 'r':
+ {
+ char str[2];
+ str[0] = c;
+ str[1] = '\0';
+ set_ordering (str, &gkey, bl_both);
+ }
+ break;
+
+ case 'c':
+ checkonly = 1;
+ break;
+
+ case 'k':
+ key = new_key ();
+
+ /* Get POS1. */
+ s = parse_field_count (optarg, &key->sword,
+ N_("invalid number at field start"));
+ if (! key->sword--)
+ {
+ /* Provoke with `sort -k0' */
+ badfieldspec (optarg, N_("field number is zero"));
+ }
if (*s == '.')
- s = parse_field_count (s + 1, &key->schar);
+ {
+ s = parse_field_count (s + 1, &key->schar,
+ N_("invalid number after `.'"));
+ if (! key->schar--)
+ {
+ /* Provoke with `sort -k1.0' */
+ badfieldspec (optarg, N_("character offset is zero"));
+ }
+ }
if (! (key->sword | key->schar))
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 (ISDIGIT (*s) || (*s == '.' && ISDIGIT (s[1])))
+ if (*s != ',')
{
- if (!key)
+ key->eword = -1;
+ key->echar = 0;
+ }
+ else
+ {
+ /* Get POS2. */
+ s = parse_field_count (s + 1, &key->eword,
+ N_("invalid number after `,'"));
+ if (! key->eword--)
{
- /* Provoke with `sort -9'. */
- error (0, 0, _("when using the old-style +POS and -POS \
-key specifiers,\nthe +POS specifier must come first"));
- usage (SORT_FAILURE);
+ /* Provoke with `sort -k1,0' */
+ badfieldspec (optarg, N_("field number is zero"));
}
- s = parse_field_count (s, &key->eword);
if (*s == '.')
- s = parse_field_count (s + 1, &key->echar);
+ s = parse_field_count (s + 1, &key->echar,
+ N_("invalid number after `.'"));
+ else
+ {
+ /* `-k 2,3' is equivalent to `+1 -3'. */
+ key->eword++;
+ }
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 (SORT_FAILURE, 0,
- _("option `-k' requires an argument"));
- else
- s = argv[++i];
- }
- if (key)
- insertkey (key);
- key = (struct keyfield *)
- xmalloc (sizeof (struct keyfield));
- key_init (key);
- /* Get POS1. */
- if (!ISDIGIT (*s))
- badfieldspec (argv[i]);
- s = parse_field_count (s, &key->sword);
- if (! key->sword--)
- {
- /* Provoke with `sort -k0' */
- error (0, 0, _("the starting field number argument \
-to the `-k' option must be positive"));
- badfieldspec (argv[i]);
- }
- if (*s == '.')
- {
- if (!ISDIGIT (s[1]))
- {
- /* Provoke with `sort -k1.' */
- error (0, 0, _("starting field spec has `.' but \
-lacks following character offset"));
- badfieldspec (argv[i]);
- }
- s = parse_field_count (s + 1, &key->schar);
- if (! key->schar--)
- {
- /* Provoke with `sort -k1.0' */
- error (0, 0, _("starting field character offset \
-argument to the `-k' option must be positive"));
- badfieldspec (argv[i]);
- }
- }
- if (! (key->sword | key->schar))
- 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. */
- s = parse_field_count (s, &key->eword);
- if (! key->eword--)
- {
- /* Provoke with `sort -k1,0' */
- error (0, 0, _("ending field number argument \
-to the `-k' option must be positive"));
- badfieldspec (argv[i]);
- }
- if (*s == '.')
- {
- if (!ISDIGIT (s[1]))
- {
- /* Provoke with `sort -k1,1.' */
- error (0, 0, _("ending field spec has `.' \
-but lacks following character offset"));
- badfieldspec (argv[i]);
- }
- s = parse_field_count (s + 1, &key->echar);
- }
- else
- {
- /* `-k 2,3' is equivalent to `+1 -3'. */
- key->eword++;
- }
- 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 (SORT_FAILURE, 0,
- _("option `-o' requires an argument"));
- else
- outfile = argv[++i];
- }
- goto outer;
- case 's':
- stable = 1;
- break;
- case 'S':
- if (s[1])
- specify_sort_size (++s);
- else if (i < argc - 1)
- specify_sort_size (argv[++i]);
- else
- error (SORT_FAILURE, 0,
- _("option `-S' requires an argument"));
- goto outer;
- case 't':
- if (s[1])
- tab = *++s;
- else if (i < argc - 1)
- {
- tab = *argv[++i];
- goto outer;
- }
- else
- error (SORT_FAILURE, 0,
- _("option `-t' requires an argument"));
- break;
- case 'T':
- if (s[1])
- add_temp_dir (++s);
- else
- {
- if (i < argc - 1)
- add_temp_dir (argv[++i]);
- else
- error (SORT_FAILURE, 0,
- _("option `-T' requires an argument"));
- }
- goto outer;
- /* break; */
- case 'u':
- unique = 1;
- break;
- case 'z':
- eolchar = 0;
- 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 (SORT_FAILURE);
- }
- if (*s)
- ++s;
- }
- }
- else /* Not an option. */
- {
- files[nfiles++] = argv[i];
+ if (*s)
+ badfieldspec (optarg, N_("stray character in field spec"));
+ insertkey (key);
+ break;
+
+ case 'm':
+ mergeonly = 1;
+ break;
+
+ case 'o':
+ outfile = optarg;
+ break;
+
+ case 's':
+ stable = 1;
+ break;
+
+ case 'S':
+ specify_sort_size (optarg);
+ break;
+
+ case 't':
+ tab = optarg[0];
+ if (tab && optarg[1])
+ {
+ /* Provoke with `sort -txx'. */
+ error (SORT_FAILURE, 0, _("multi-character tab `%s'"), optarg);
+ }
+ break;
+
+ case 'T':
+ add_temp_dir (optarg);
+ break;
+
+ case 'u':
+ unique = 1;
+ break;
+
+ case 'y':
+ /* Accept and ignore e.g. -y0 for compatibility with Solaris
+ 2.x through Solaris 7. -y is marked as obsolete starting
+ with Solaris 8. */
+ break;
+
+ case 'z':
+ eolchar = 0;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (SORT_FAILURE);
}
- outer:;
}
- if (key)
- insertkey (key);
-
/* Inheritance of global options to individual keys. */
for (key = keylist; key; key = key->next)
if (!key->ignore && !key->translate && !key->skipsblanks && !key->reverse
@@ -2451,9 +2455,8 @@ but lacks following character offset"));
if (checkonly)
{
if (nfiles > 1)
- error (SORT_FAILURE, 0,
- _("too many arguments; with -c, there may be at most\
- one file argument"));
+ error (SORT_FAILURE, 0, _("extra operand `%s' not allowed with -c"),
+ files[1]);
/* POSIX requires that sort return 1 IFF invoked with -c and the
input is not properly sorted. */