Fix #190: support version sort (option -n)

This commit is contained in:
Arun Prakash Jana 2019-02-05 23:48:34 +05:30
parent b716cac0c9
commit 336eca300a
No known key found for this signature in database
GPG key ID: A75979F35C080412
3 changed files with 106 additions and 5 deletions

View file

@ -82,7 +82,8 @@ It runs on Linux, macOS, Raspberry Pi, BSD, Cygwin, Linux subsystem for Windows
- Sorting - Sorting
- Directories always listed on top - Directories always listed on top
- Sort by file name, modification time, size - Sort by file name, modification time, size
- Numeric order for numeric names (visit _/proc_) - Numeric order for pure numeric names (visit _/proc_)
- Version sort
- Search - Search
- Instant filtering with *search-as-you-type* - Instant filtering with *search-as-you-type*
- Regex and substring match - Regex and substring match
@ -187,7 +188,7 @@ Search keyword and option completion scripts for Bash, Fish and Zsh can be found
#### Cmdline options #### Cmdline options
``` ```
usage: nnn [-b key] [-C] [-e] [-i] [-l] usage: nnn [-b key] [-C] [-e] [-i] [-l] [-n]
[-p file] [-S] [-v] [-h] [PATH] [-p file] [-S] [-v] [-h] [PATH]
The missing terminal file manager for X. The missing terminal file manager for X.
@ -201,6 +202,7 @@ optional args:
-e use exiftool for media info -e use exiftool for media info
-i nav-as-you-type mode -i nav-as-you-type mode
-l light mode -l light mode
-n use version compare to sort
-p file selection file (stdout if '-') -p file selection file (stdout if '-')
-S disk usage mode -S disk usage mode
-v show version -v show version

3
nnn.1
View file

@ -181,6 +181,9 @@ supports the following options:
.Fl l .Fl l
start in light mode (fewer details) start in light mode (fewer details)
.Pp .Pp
.Fl n
use version compare to sort files
.Pp
.Fl "p file" .Fl "p file"
copy (or \fIpick\fR) selection to file, or stdout if file='-' copy (or \fIpick\fR) selection to file, or stdout if file='-'
.Pp .Pp

102
src/nnn.c
View file

@ -202,6 +202,18 @@ disabledbg()
#define POLYNOMIAL 0xD8 /* 11011 followed by 0's */ #define POLYNOMIAL 0xD8 /* 11011 followed by 0's */
#define CRC8_TABLE_LEN 256 #define CRC8_TABLE_LEN 256
/* Version compare macros */
/* states: S_N: normal, S_I: comparing integral part, S_F: comparing
fractionnal parts, S_Z: idem but with leading Zeroes only */
#define S_N 0x0
#define S_I 0x3
#define S_F 0x6
#define S_Z 0x9
/* result_type: VCMP: return diff; VLEN: compare using len_diff/diff */
#define VCMP 2
#define VLEN 3
/* Volume info */ /* Volume info */
#define FREE 0 #define FREE 0
#define CAPACITY 1 #define CAPACITY 1
@ -1176,6 +1188,86 @@ static int xstricmp(const char * const s1, const char * const s2)
return strcoll(s1, s2); return strcoll(s1, s2);
} }
/*
* Version comparison
*
* The code for version compare is a modified version of the GLIBC
* and uClibc implementation of strverscmp(). The source is here:
* https://elixir.bootlin.com/uclibc-ng/latest/source/libc/string/strverscmp.c
*/
/*
* Compare S1 and S2 as strings holding indices/version numbers,
* returning less than, equal to or greater than zero if S1 is less than,
* equal to or greater than S2 (for more info, see the texinfo doc).
*/
static int xstrverscmp(const char * const s1, const char * const s2)
{
static const uchar *p1;
static const uchar *p2;
static uchar c1, c2;
static int state, diff;
p1 = (const uchar *)s1;
p2 = (const uchar *)s2;
/* Symbol(s) 0 [1-9] others
Transition (10) 0 (01) d (00) x */
static const uint8_t next_state[] =
{
/* state x d 0 */
/* S_N */ S_N, S_I, S_Z,
/* S_I */ S_N, S_I, S_I,
/* S_F */ S_N, S_F, S_F,
/* S_Z */ S_N, S_F, S_Z
};
static const int8_t result_type[] =
{
/* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */
/* S_N */ VCMP, VCMP, VCMP, VCMP, VLEN, VCMP, VCMP, VCMP, VCMP,
/* S_I */ VCMP, -1, -1, +1, VLEN, VLEN, +1, VLEN, VLEN,
/* S_F */ VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, VCMP,
/* S_Z */ VCMP, +1, +1, -1, VCMP, VCMP, -1, VCMP, VCMP
};
if (p1 == p2)
return 0;
c1 = *p1++;
c2 = *p2++;
/* Hint: '0' is a digit too. */
state = S_N + ((c1 == '0') + (xisdigit(c1) != 0));
while ((diff = c1 - c2) == 0) {
if (c1 == '\0')
return diff;
state = next_state[state];
c1 = *p1++;
c2 = *p2++;
state += (c1 == '0') + (xisdigit(c1) != 0);
}
state = result_type[state * 3 + (((c2 == '0') + (xisdigit(c2) != 0)))];
switch (state) {
case VCMP:
return diff;
case VLEN:
while (xisdigit (*p1++))
if (!xisdigit (*p2++))
return 1;
return xisdigit (*p2) ? -1 : diff;
default:
return state;
}
}
static int (*cmpfn)(const char * const s1, const char * const s2) = &xstricmp;
/* Return the integer value of a char representing HEX */ /* Return the integer value of a char representing HEX */
static char xchartohex(char c) static char xchartohex(char c)
{ {
@ -1251,7 +1343,7 @@ static int entrycmp(const void *va, const void *vb)
return -1; return -1;
} }
return xstricmp(pa->name, pb->name); return cmpfn(pa->name, pb->name);
} }
/* /*
@ -3863,7 +3955,7 @@ nochange:
static void usage(void) static void usage(void)
{ {
fprintf(stdout, fprintf(stdout,
"usage: nnn [-b key] [-C] [-e] [-i] [-l]\n" "usage: nnn [-b key] [-C] [-e] [-i] [-l] [-n]\n"
" [-p file] [-S] [-v] [-h] [PATH]\n\n" " [-p file] [-S] [-v] [-h] [PATH]\n\n"
"The missing terminal file manager for X.\n\n" "The missing terminal file manager for X.\n\n"
"positional args:\n" "positional args:\n"
@ -3874,6 +3966,7 @@ static void usage(void)
" -e use exiftool for media info\n" " -e use exiftool for media info\n"
" -i nav-as-you-type mode\n" " -i nav-as-you-type mode\n"
" -l light mode\n" " -l light mode\n"
" -n use version compare to sort\n"
" -p file selection file (stdout if '-')\n" " -p file selection file (stdout if '-')\n"
" -S disk usage mode\n" " -S disk usage mode\n"
" -v show version\n" " -v show version\n"
@ -3887,7 +3980,7 @@ int main(int argc, char *argv[])
char *ipath = NULL; char *ipath = NULL;
int opt; int opt;
while ((opt = getopt(argc, argv, "Slib:Cep:vh")) != -1) { while ((opt = getopt(argc, argv, "Slib:Cenp:vh")) != -1) {
switch (opt) { switch (opt) {
case 'S': case 'S':
cfg.blkorder = 1; cfg.blkorder = 1;
@ -3910,6 +4003,9 @@ int main(int argc, char *argv[])
case 'e': case 'e':
cfg.metaviewer = EXIFTOOL; cfg.metaviewer = EXIFTOOL;
break; break;
case 'n':
cmpfn = &xstrverscmp;
break;
case 'p': case 'p':
cfg.picker = 1; cfg.picker = 1;
if (optarg[0] == '-' && optarg[1] == '\0') if (optarg[0] == '-' && optarg[1] == '\0')