initial commit
This commit is contained in:
163
coreutils-sort.cc
Normal file
163
coreutils-sort.cc
Normal file
@@ -0,0 +1,163 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
Origin: coreutils-9.7/lib/filevercmp.c
|
||||
Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
|
||||
Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
|
||||
Copyright (C) 2008-2025 Free Software Foundation, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "coreutils-sort.hh"
|
||||
|
||||
/* Return the length of a prefix of S that corresponds to the suffix
|
||||
defined by this extended regular expression in the C locale:
|
||||
(\.[A-Za-z~][A-Za-z0-9~]*)*$
|
||||
Use the longest suffix matching this regular expression,
|
||||
except do not use all of S as a suffix if S is nonempty.
|
||||
If *LEN is -1, S is a string; set *LEN to S's length.
|
||||
Otherwise, *LEN should be nonnegative, S is a char array,
|
||||
and *LEN does not change. */
|
||||
static
|
||||
ptrdiff_t file_prefixlen(char const * s, ptrdiff_t * len)
|
||||
{
|
||||
size_t n = *len; /* SIZE_MAX if N == -1. */
|
||||
ptrdiff_t prefixlen = 0;
|
||||
|
||||
for (ptrdiff_t i = 0; ; ) {
|
||||
if ((*len < 0) ? !s[i] : (i == n)) {
|
||||
*len = i;
|
||||
return prefixlen;
|
||||
}
|
||||
|
||||
i++;
|
||||
prefixlen = i;
|
||||
while ((i + 1 < n) && (s[i] == '.') && (isalpha(s[i + 1]) || (s[i + 1] == '~'))) {
|
||||
for (i += 2; (i < n) && (isalnum(s[i]) || (s[i] == '~')); i++) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a version sort comparison value for S's byte at position POS.
|
||||
S has length LEN. If POS == LEN, sort before all non-'~' bytes. */
|
||||
static
|
||||
int order(char const * s, ptrdiff_t pos, ptrdiff_t len)
|
||||
{
|
||||
if (pos == len) return -1;
|
||||
|
||||
unsigned char c = s[pos];
|
||||
if (isdigit(c)) return 0;
|
||||
else if (isalpha(c)) return c;
|
||||
else if (c == '~') return -2;
|
||||
else
|
||||
{
|
||||
static_assert (UCHAR_MAX <= (INT_MAX - 1 - 2) / 2);
|
||||
return c + UCHAR_MAX + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* slightly modified verrevcmp function from dpkg
|
||||
S1, S2 - compared char array
|
||||
S1_LEN, S2_LEN - length of arrays to be scanned
|
||||
|
||||
This implements the algorithm for comparison of version strings
|
||||
specified by Debian and now widely adopted. The detailed
|
||||
specification can be found in the Debian Policy Manual in the
|
||||
section on the 'Version' control field. This version of the code
|
||||
implements that from s5.6.12 of Debian Policy v3.8.0.1
|
||||
https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version */
|
||||
static
|
||||
int verrevcmp(const char * s1, ptrdiff_t s1_len, const char * s2, ptrdiff_t s2_len)
|
||||
{
|
||||
ptrdiff_t s1_pos = 0;
|
||||
ptrdiff_t s2_pos = 0;
|
||||
|
||||
while (s1_pos < s1_len || s2_pos < s2_len) {
|
||||
int first_diff = 0;
|
||||
while (((s1_pos < s1_len) && !isdigit(s1[s1_pos])) || ((s2_pos < s2_len) && !isdigit(s2[s2_pos]))) {
|
||||
int s1_c = order(s1, s1_pos, s1_len);
|
||||
int s2_c = order(s2, s2_pos, s2_len);
|
||||
if (s1_c != s2_c) return s1_c - s2_c;
|
||||
s1_pos++;
|
||||
s2_pos++;
|
||||
}
|
||||
|
||||
while ((s1_pos < s1_len) && (s1[s1_pos] == '0')) {
|
||||
s1_pos++;
|
||||
}
|
||||
while ((s2_pos < s2_len) && (s2[s2_pos] == '0')) {
|
||||
s2_pos++;
|
||||
}
|
||||
while ((s1_pos < s1_len) && (s2_pos < s2_len) && isdigit(s1[s1_pos]) && isdigit(s2[s2_pos])) {
|
||||
if (!first_diff) first_diff = s1[s1_pos] - s2[s2_pos];
|
||||
s1_pos++;
|
||||
s2_pos++;
|
||||
}
|
||||
|
||||
if ((s1_pos < s1_len) && isdigit(s1[s1_pos])) return 1;
|
||||
if ((s2_pos < s2_len) && isdigit(s2[s2_pos])) return -1;
|
||||
if (first_diff) return first_diff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int filenvercmp (char const * a, ptrdiff_t alen, char const * b, ptrdiff_t blen)
|
||||
{
|
||||
/* Special case for empty versions. */
|
||||
bool aempty = (alen < 0) ? !a[0] : !alen;
|
||||
bool bempty = (blen < 0) ? !b[0] : !blen;
|
||||
if (aempty) return -!bempty;
|
||||
if (bempty) return 1;
|
||||
|
||||
/* Special cases for leading ".": "." sorts first, then "..", then
|
||||
other names with leading ".", then other names. */
|
||||
if (a[0] == '.') {
|
||||
if (b[0] != '.') return -1;
|
||||
|
||||
bool adot = alen < 0 ? !a[1] : alen == 1;
|
||||
bool bdot = blen < 0 ? !b[1] : blen == 1;
|
||||
if (adot) return -!bdot;
|
||||
if (bdot) return 1;
|
||||
|
||||
bool adotdot = (a[1] == '.') && ((alen < 0) ? !a[2] : (alen == 2));
|
||||
bool bdotdot = (b[1] == '.') && ((blen < 0) ? !b[2] : (blen == 2));
|
||||
if (adotdot) return -!bdotdot;
|
||||
if (bdotdot) return 1;
|
||||
}
|
||||
else if (b[0] == '.') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Cut file suffixes. */
|
||||
ptrdiff_t aprefixlen = file_prefixlen(a, &alen);
|
||||
ptrdiff_t bprefixlen = file_prefixlen(b, &blen);
|
||||
|
||||
/* If both suffixes are empty, a second pass would return the same thing. */
|
||||
bool one_pass_only = (aprefixlen == alen) && (bprefixlen == blen);
|
||||
|
||||
int result = verrevcmp(a, aprefixlen, b, bprefixlen);
|
||||
|
||||
/* Return the initial result if nonzero, or if no second pass is needed.
|
||||
Otherwise, restore the suffixes and try again. */
|
||||
return (result || one_pass_only) ? result : verrevcmp(a, alen, b, blen);
|
||||
}
|
||||
|
||||
static inline
|
||||
int filevercmp(const char * s1, const char * s2)
|
||||
{
|
||||
return filenvercmp(s1, -1, s2, -1);
|
||||
}
|
||||
|
||||
int coreutils_version_sort(char const * a, char const * b)
|
||||
{
|
||||
return filevercmp(a, b);
|
||||
}
|
Reference in New Issue
Block a user