mirror of
https://github.com/jarun/nnn.git
synced 2024-12-01 02:49:44 +00:00
Fix #190: support version sort (option -n)
This commit is contained in:
parent
b716cac0c9
commit
336eca300a
|
@ -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
3
nnn.1
|
@ -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
102
src/nnn.c
|
@ -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')
|
||||||
|
|
Loading…
Reference in a new issue