241 lines
6.6 KiB
C
241 lines
6.6 KiB
C
#include "cmd.h"
|
|
#include "sort.h"
|
|
#include "efm.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
// sorting helpers
|
|
enum
|
|
{
|
|
SORT_LESS = -1,
|
|
SORT_MORE = 1,
|
|
SORT_INVALID = -100
|
|
};
|
|
|
|
static char
|
|
_str_mode_file_type(int mode)
|
|
{
|
|
char c = '?';
|
|
|
|
if (S_ISREG(mode)) c = '-';
|
|
else if (S_ISDIR(mode)) c = 'd';
|
|
else if (S_ISBLK(mode)) c = 'b';
|
|
else if (S_ISCHR(mode)) c = 'c';
|
|
#ifdef S_ISFIFO
|
|
else if (S_ISFIFO(mode)) c = 'p';
|
|
#endif
|
|
#ifdef S_ISLNK
|
|
else if (S_ISLNK(mode)) c = 'l';
|
|
#endif
|
|
#ifdef S_ISSOCK
|
|
else if (S_ISSOCK(mode)) c = 's';
|
|
#endif
|
|
#ifdef S_ISDOOR
|
|
else if (S_ISDOOR(mode)) c = 'D';
|
|
#endif
|
|
return c;
|
|
}
|
|
|
|
static void
|
|
_str_mode(int mode, char str[11])
|
|
{ // generate string mode i9nto dest str buffer
|
|
static const char *rwx[] = {
|
|
"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"
|
|
};
|
|
|
|
str[0] = _str_mode_file_type(mode);
|
|
strcpy(&str[1], rwx[(mode >> 6)& 7]);
|
|
strcpy(&str[4], rwx[(mode >> 3)& 7]);
|
|
strcpy(&str[7], rwx[(mode & 7)]);
|
|
if (mode & S_ISUID) str[3] = (mode & S_IXUSR) ? 's' : 'S';
|
|
if (mode & S_ISGID) str[6] = (mode & S_IXGRP) ? 's' : 'l';
|
|
if (mode & S_ISVTX) str[9] = (mode & S_IXOTH) ? 't' : 'T';
|
|
str[10] = 0;
|
|
}
|
|
|
|
static int
|
|
_sort_str_field(const Cmd *c1, const Cmd *c2, int (*cmpfn) (const char *s1, const char *s2), const char *field)
|
|
{ // a string fields - get it and sort using compare func
|
|
const char *field1, *field2, *path1, *path2;
|
|
int res;
|
|
|
|
field1 = cmd_key_find(c1, field);
|
|
if (!field1) return 0;
|
|
|
|
field2 = cmd_key_find(c2, field);
|
|
if (!field2) return 0;
|
|
|
|
res = cmpfn(field1, field2);
|
|
if (!res)
|
|
{ // they are the same string! use pure filename path as it's unique
|
|
path1 = cmd_key_find(c1, "path");
|
|
path2 = cmd_key_find(c2, "path");
|
|
res = cmpfn(path1, path2);
|
|
if (!res) return strcmp(path1, path2);
|
|
return res;
|
|
}
|
|
return cmpfn(field1, field2);
|
|
}
|
|
|
|
static int
|
|
_sort_num_field(const Cmd *c1, const Cmd *c2, const char *field)
|
|
{ // user a named field that is a number like mtime, ctime, size etc ...
|
|
const char *f, *path1, *path2;
|
|
unsigned long long field1 = 0, field2 = 0;
|
|
|
|
// get field and convert to ulonglong
|
|
f = cmd_key_find(c1, field);
|
|
if (f) field1 = strtoull(f, NULL, 10);
|
|
|
|
// get field and convert to ulonglong
|
|
f = cmd_key_find(c2, field);
|
|
if (f) field2 = strtoull(f, NULL, 10);
|
|
|
|
if (field1 == field2)
|
|
{ // they are the same string! use pure filename path as it's unique
|
|
path1 = cmd_key_find(c1, "path");
|
|
path2 = cmd_key_find(c2, "path");
|
|
return strcmp(path1, path2);
|
|
}
|
|
// sort result
|
|
if (field1 > field2) return SORT_MORE;
|
|
return SORT_LESS;
|
|
}
|
|
|
|
static int
|
|
_sort_path(const Cmd *c1, const Cmd *c2, int (*cmpfn) (const char *s1, const char *s2))
|
|
{ // use pure filename path not label
|
|
return _sort_str_field(c1, c2, cmpfn, "path");
|
|
}
|
|
|
|
static int
|
|
_sort_label(const Cmd *c1, const Cmd *c2, int (*cmpfn) (const char *s1, const char *s2))
|
|
{ // sort by label or path - some files like .desktop files have labels
|
|
const char *path1, *path2;
|
|
int res;
|
|
|
|
// get a label instead of filename path element if available
|
|
path1 = cmd_key_find(c1, "label");
|
|
if (!path1) path1 = cmd_key_find(c1, "path");
|
|
|
|
// get a label instead of filename path element if available
|
|
path2 = cmd_key_find(c2, "label");
|
|
if (!path2) path2 = cmd_key_find(c2, "path");
|
|
|
|
res = cmpfn(path1, path2);
|
|
if (!res)
|
|
{ // they are the same string! use pure filename path as it's unique
|
|
path1 = cmd_key_find(c1, "path");
|
|
path2 = cmd_key_find(c2, "path");
|
|
res = cmpfn(path1, path2);
|
|
if (!res) return strcmp(path1, path2);
|
|
return res;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static int
|
|
_sort_mode(const Cmd *c1, const Cmd *c2)
|
|
{ // sort by mode string like "drwxr-xr-x" or "-rw-r--r--"
|
|
const char *f;
|
|
int mode1 = 0, mode2 = 0, ret = 0;
|
|
char m1[11], m2[11];
|
|
|
|
// get mdoe and generate mode string
|
|
f = cmd_key_find(c1, "mode");
|
|
if (f) mode1 = strtoull(f, NULL, 16);
|
|
_str_mode(mode1, m1);
|
|
|
|
// get mdoe and generate mode string
|
|
f = cmd_key_find(c2, "mode");
|
|
if (f) mode2 = strtoull(f, NULL, 16);
|
|
_str_mode(mode2, m2);
|
|
|
|
// sort by the mode string
|
|
ret = strcmp(m1, m2);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_sort_dir_not_dir(const Cmd *c1, const Cmd *c2)
|
|
{
|
|
const char *type1, *type2;
|
|
|
|
// get file types for both - and if link resolve the link dest type
|
|
type1 = cmd_key_find(c1, "type");
|
|
if ((type1) && (!strcmp(type1, "link")))
|
|
type1 = cmd_key_find(c1, "link-type");
|
|
if (!type1) type1 = "file";
|
|
|
|
type2 = cmd_key_find(c2, "type");
|
|
if ((type2) && (!strcmp(type2, "link")))
|
|
type2 = cmd_key_find(c2, "link-type");
|
|
if (!type2) type2 = "file";
|
|
|
|
// 1st is dir, 2nd is not dir
|
|
if (( !strcmp(type1, "dir")) && (!!strcmp(type2, "dir"))) return SORT_LESS;
|
|
// 1st is not dir, 2nd is dir
|
|
if ((!!strcmp(type1, "dir")) && ( !strcmp(type2, "dir"))) return SORT_MORE;
|
|
// both are dir or both are not dir
|
|
return SORT_INVALID;
|
|
}
|
|
|
|
// sorter logic
|
|
static int
|
|
_sort_cmd_do(const Cmd *c1, const Cmd *c2)
|
|
{
|
|
int dir_not_dir = SORT_INVALID;
|
|
Efm_Sort_Mode sort_mode;
|
|
int (*cmpfn) (const char *s1, const char *s2) = strcmp;
|
|
Eina_Bool label_sort = EINA_FALSE;
|
|
|
|
sort_mode = c1->sort_mode;
|
|
// handle flags
|
|
if (sort_mode & EFM_SORT_MODE_DIRS_FIRST)
|
|
dir_not_dir = _sort_dir_not_dir(c1, c2);
|
|
if (sort_mode & EFM_SORT_MODE_NOCASE)
|
|
cmpfn = strcasecmp;
|
|
if (sort_mode & EFM_SORT_MODE_LABEL_NOT_PATH)
|
|
label_sort = EINA_TRUE;
|
|
|
|
// handle each sort mode
|
|
|
|
// if one is a dir and one is not - other sorting compares are not useful
|
|
// so just return this status
|
|
if (dir_not_dir != SORT_INVALID) return dir_not_dir;
|
|
|
|
// handle the actual specific sort field to use
|
|
switch (sort_mode & EFM_SORT_MODE_MASK)
|
|
{
|
|
case EFM_SORT_MODE_NAME:
|
|
if (label_sort) return _sort_label(c1, c2, cmpfn);
|
|
return _sort_path(c1, c2, cmpfn);
|
|
case EFM_SORT_MODE_SIZE:
|
|
return _sort_num_field(c1, c2, "size");
|
|
case EFM_SORT_MODE_DATE:
|
|
return _sort_num_field(c1, c2, "mtime");
|
|
case EFM_SORT_MODE_MIME:
|
|
return _sort_str_field(c1, c2, strcmp, "mime");
|
|
case EFM_SORT_MODE_USER:
|
|
return _sort_str_field(c1, c2, strcmp, "user");
|
|
case EFM_SORT_MODE_GROUP:
|
|
return _sort_str_field(c1, c2, strcmp, "group");
|
|
case EFM_SORT_MODE_PERMISSIONS:
|
|
return _sort_mode(c1, c2);
|
|
default: // unknown - so just plain path
|
|
goto def;
|
|
break;
|
|
}
|
|
def:
|
|
return _sort_path(c1, c2, cmpfn);
|
|
}
|
|
|
|
int
|
|
sort_cmd(const void *c1, const void *c2)
|
|
{
|
|
return _sort_cmd_do(c1, c2);
|
|
}
|