Here is the usage text from my latest --sort=version patch (attached),
now that paperwork is in order.
Cheers - Bruce

$ ./sort --help
Usage: ./sort [OPTION]... [FILE]...
Write sorted concatenation of all FILE(s) to standard output.

Mandatory arguments to long options are mandatory for short options too.
Ordering options:

  -b, --ignore-leading-blanks  ignore leading blanks
  -d, --dictionary-order      consider only blanks and alphanumeric characters
  -f, --ignore-case           fold lower case to upper case characters
  -g, --general-numeric-sort  compare according to general numerical value
  -i, --ignore-nonprinting    consider only printable characters
  -M, --month-sort            compare (unknown) < `JAN' < ... < `DEC'
  -n, --numeric-sort          compare according to string numerical value
  -R, --random-sort           sort by random hash of keys
  -V, --version-sort          sort by numeric version (see strverscmp(3C))
      --random-source=FILE    get random bytes from FILE (default /dev/urandom)
      --sort=WORD             sort according to WORD:
                                general-numeric -g, month -M, numeric -n,
                                random -R, version -V
  -r, --reverse               reverse the result of comparisons

Other options:

  -c, --check, --check=diagnose-first  check for sorted input; do not sort
  -C, --check=quiet, --check=silent  like -c, but do not report first bad line
      --compress-program=PROG  compress temporaries with PROG;
                              decompress them with PROG -d
  -k, --key=POS1[,POS2]     start a key at POS1, end it at POS2 (origin 1)
  -m, --merge               merge already sorted files; do not sort
  -o, --output=FILE         write result to FILE instead of standard output
  -s, --stable              stabilize sort by disabling last-resort comparison
  -S, --buffer-size=SIZE    use SIZE for main memory buffer
  -t, --field-separator=SEP  use SEP instead of non-blank to blank transition
  -T, --temporary-directory=DIR  use DIR for temporaries, not $TMPDIR or /tmp;
                              multiple options specify multiple directories
  -u, --unique              with -c, check for strict ordering;
                              without -c, output only the first of an equal run
  -z, --zero-terminated     end lines with 0 byte, not newline
      --help     display this help and exit
      --version  output version information and exit

POS is F[.C][OPTS], where F is the field number and C the character position
in the field; both are origin 1.  If neither -t nor -b is in effect, characters
in a field are counted from the beginning of the preceding whitespace.  OPTS is
one or more single-letter ordering options, which override global ordering
options for that key.  If no key is given, use the entire line as the key.

SIZE may be followed by the following multiplicative suffixes:
% 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y.

With no FILE, or when FILE is -, read standard input.

*** WARNING ***
The locale specified by the environment affects sort order.
Set LC_ALL=C to get the traditional sort order that uses
native byte values.

Report bugs to <bug-coreutils@gnu.org>.
diff --git a/src/sort.c b/src/sort.c
index 8b2eec5..adb4589 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -172,6 +172,7 @@ struct keyfield
 				   Handle numbers in exponential notation. */
   bool month;			/* Flag for comparison by month name. */
   bool reverse;			/* Reverse the sense of comparison. */
+  bool version;			/* sort by version number */
   struct keyfield *next;	/* Next keyfield to try. */
 };
 
@@ -328,10 +329,11 @@ Ordering options:\n\
   -M, --month-sort            compare (unknown) < `JAN' < ... < `DEC'\n\
   -n, --numeric-sort          compare according to string numerical value\n\
   -R, --random-sort           sort by random hash of keys\n\
+  -V, --version-sort          sort by numeric version (see strverscmp(3C))\n\
       --random-source=FILE    get random bytes from FILE (default /dev/urandom)\n\
       --sort=WORD             sort according to WORD:\n\
                                 general-numeric -g, month -M, numeric -n,\n\
-                                random -R\n\
+                                random -R, version -V\n\
   -r, --reverse               reverse the result of comparisons\n\
 \n\
 "), stdout);
@@ -398,7 +400,7 @@ enum
   SORT_OPTION
 };
 
-static char const short_options[] = "-bcCdfgik:mMno:rRsS:t:T:uy:z";
+static char const short_options[] = "-bcCdfgik:mMno:rRsS:t:T:uVy:z";
 
 static struct option const long_options[] =
 {
@@ -423,31 +425,50 @@ static struct option const long_options[] =
   {"field-separator", required_argument, NULL, 't'},
   {"temporary-directory", required_argument, NULL, 'T'},
   {"unique", no_argument, NULL, 'u'},
+  {"compare-version", no_argument, NULL, 'V'},
   {"zero-terminated", no_argument, NULL, 'z'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0},
 };
 
+#define CHECK_TABLE \
+  _ct_("quiet",          'C') \
+  _ct_("silent",         'C') \
+  _ct_("diagnose-first", 'c')
+
 static char const *const check_args[] =
 {
-  "quiet", "silent", "diagnose-first", NULL
+#define _ct_(_s, _c) _s,
+  CHECK_TABLE NULL
+#undef  _ct_
 };
 static char const check_types[] =
 {
-  'C', 'C', 'c'
+#define _ct_(_s, _c) _c,
+  CHECK_TABLE
+#undef  _ct_
 };
-ARGMATCH_VERIFY (check_args, check_types);
+
+#define SORT_TABLE \
+  _st_("general-numeric", 'g') \
+  _st_("month",           'M') \
+  _st_("numeric",         'n') \
+  _st_("random",          'R') \
+  _st_("version",         'V')
 
 static char const *const sort_args[] =
 {
-  "general-numeric", "month", "numeric", "random", NULL
+#define _st_(_s, _c) #_s,
+  SORT_TABLE NULL
+#undef  _st_
 };
 static char const sort_types[] =
 {
-  'g', 'M', 'n', 'R'
+#define _st_(_s, _c) _c,
+  SORT_TABLE
+#undef  _st_
 };
-ARGMATCH_VERIFY (sort_args, sort_types);
 
 /* The set of signals that are caught.  */
 static sigset_t caught_signals;
@@ -1717,6 +1738,32 @@ compare_random (char *restrict texta, size_t lena,
   return diff;
 }
 
+/* Compare the keys TEXTA (of length LENA) and TEXTB (of length LENB)
+   using strverscmp.  */
+
+static int
+compare_version (char *restrict texta, size_t lena,
+                char *restrict textb, size_t lenb)
+{
+  int diff;
+
+  /*
+   *  It is necessary to save the character after the end of the field.
+   *  "strverscmp" works with NUL terminated strings.  Our blocks of
+   *  text are not necessarily terminated with a NUL byte.
+   */
+  char sv_a = texta[lena];
+  char sv_b = textb[lenb];
+
+  texta[lena] = textb[lenb] = '\0';
+  diff = strverscmp (texta, textb);
+
+  texta[lena] = sv_a;
+  textb[lenb] = sv_b;
+
+  return diff;
+}
+
 /* Compare two lines A and B trying every key in sequence until there
    are no more keys or a difference is found. */
 
@@ -1756,6 +1803,10 @@ keycompare (const struct line *a, const struct line *b)
 		  (texta, textb));
 	  *lima = savea, *limb = saveb;
 	}
+
+      else if (key->version)
+        diff = compare_version (texta, lena, textb, lenb);
+
       else if (key->month)
 	diff = getmonth (texta, lena) - getmonth (textb, lenb);
       /* Sorting like this may become slow, so in a simple locale the user
@@ -2602,10 +2653,11 @@ check_ordering_compatibility (void)
 
   for (key = keylist; key; key = key->next)
     if ((1 < (key->random + key->numeric + key->general_numeric + key->month
-	      + !!key->ignore))
+	      + key->version + !!key->ignore))
 	|| (key->random && key->translate))
       {
-	char opts[7];
+        /* The following is too big, but guaranteed to be "big enough". */
+	char opts[sizeof short_options];
 	char *p = opts;
 	if (key->ignore == nondictionary)
 	  *p++ = 'd';
@@ -2619,6 +2671,8 @@ check_ordering_compatibility (void)
 	  *p++ = 'M';
 	if (key->numeric)
 	  *p++ = 'n';
+	if (key->version)
+	  *p++ = 'V';
 	if (key->random)
 	  *p++ = 'R';
 	*p = '\0';
@@ -2720,6 +2774,9 @@ set_ordering (const char *s, struct keyfield *key, enum blanktype blanktype)
 	case 'r':
 	  key->reverse = true;
 	  break;
+	case 'V':
+	  key->version = true;
+	  break;
 	default:
 	  return (char *) s;
 	}
@@ -2845,7 +2902,7 @@ main (int argc, char **argv)
   gkey.sword = gkey.eword = SIZE_MAX;
   gkey.ignore = NULL;
   gkey.translate = NULL;
-  gkey.numeric = gkey.general_numeric = gkey.random = false;
+  gkey.numeric = gkey.general_numeric = gkey.random = gkey.version = false;
   gkey.month = gkey.reverse = false;
   gkey.skipsblanks = gkey.skipeblanks = false;
 
@@ -3102,11 +3159,16 @@ main (int argc, char **argv)
   /* Inheritance of global options to individual keys. */
   for (key = keylist; key; key = key->next)
     {
-      if (! (key->ignore || key->translate
-             || (key->skipsblanks | key->reverse
-                 | key->skipeblanks | key->month | key->numeric
-                 | key->general_numeric
-                 | key->random)))
+      if (! (key->ignore
+	     || key->translate
+	     || (key->skipsblanks
+		 | key->reverse
+		 | key->skipeblanks
+		 | key->month
+		 | key->numeric
+		 | key->version
+		 | key->general_numeric
+		 | key->random)))
         {
           key->ignore = gkey.ignore;
           key->translate = gkey.translate;
@@ -3117,15 +3179,21 @@ main (int argc, char **argv)
           key->general_numeric = gkey.general_numeric;
           key->random = gkey.random;
           key->reverse = gkey.reverse;
+          key->version = gkey.version;
         }
 
       need_random |= key->random;
     }
 
-  if (!keylist && (gkey.ignore || gkey.translate
-		   || (gkey.skipsblanks | gkey.skipeblanks | gkey.month
-		       | gkey.numeric | gkey.general_numeric
-                       | gkey.random)))
+  if (!keylist && (gkey.ignore
+		   || gkey.translate
+		   || (gkey.skipsblanks
+		       | gkey.skipeblanks
+		       | gkey.month
+		       | gkey.numeric
+		       | gkey.general_numeric
+		       | gkey.random
+		       | gkey.version)))
     {
       insertkey (&gkey);
       need_random |= gkey.random;
_______________________________________________
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils

Reply via email to