efm2/src/efm/sort.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);
}