mblaze/msort.c
2016-07-20 15:39:24 +02:00

320 lines
6.1 KiB
C

#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "blaze822.h"
struct mail {
char *file;
long idx;
union {
long intkey;
char *strkey;
} k;
};
struct mail *mails;
ssize_t mailalloc = 1024;
int idx = 0;
int (*sortorder)(const void *, const void *);
int rflag;
char *
fetch_subj(char *file)
{
struct message *msg = blaze822(file);
if (!msg)
return " (error)";
char *v = blaze822_hdr(msg, "subject");
if (!v) {
blaze822_free(msg);
return " (no subject)";
}
char *ov = 0;
char *s;
while (v != ov) {
while (*v == ' ')
v++;
if (strncasecmp(v, "Re:", 3) == 0)
v += 3;
if (strncasecmp(v, "Aw:", 3) == 0)
v += 3;
if (strncasecmp(v, "Sv:", 3) == 0)
v += 3;
if (strncasecmp(v, "Wg:", 3) == 0)
v += 3;
if (strncasecmp(v, "Fwd:", 4) == 0)
v += 4;
// XXX skip [prefix]?
ov = v;
}
s = strdup(v);
blaze822_free(msg);
return s;
}
char *
fetch_from(char *file)
{
char *from = " (unknown)";
struct message *msg = blaze822(file);
if (!msg)
return " (error)";
char *v = blaze822_hdr(msg, "from");
if (v) {
char *disp, *addr;
blaze822_addr(v, &disp, &addr);
if (*disp)
from = strdup(disp);
else if (*addr)
from = strdup(addr);
}
blaze822_free(msg);
return from;
}
time_t
fetch_date(char *file)
{
time_t t = -1;
struct message *msg = blaze822(file);
if (!msg)
return -1;
char *v = blaze822_hdr(msg, "date");
if (v)
t = blaze822_date(v);
blaze822_free(msg);
return t;
}
time_t
fetch_mtime(char *file)
{
struct stat st;
if (stat(file, &st) < 0)
return -1;
return st.st_mtime;
}
off_t
fetch_size(char *file)
{
struct stat st;
if (stat(file, &st) < 0)
return 0;
return st.st_size;
}
int
subjorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
if (!ia->k.strkey)
ia->k.strkey = fetch_subj(ia->file);
if (!ib->k.strkey)
ib->k.strkey = fetch_subj(ib->file);
int k = strcasecmp(ia->k.strkey, ib->k.strkey);
if (k == 0)
return ia->idx - ib->idx; // XXX verify
return k;
}
int
fromorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
if (!ia->k.strkey)
ia->k.strkey = fetch_from(ia->file);
if (!ib->k.strkey)
ib->k.strkey = fetch_from(ib->file);
int k = strcasecmp(ia->k.strkey, ib->k.strkey);
if (k == 0)
return ia->idx - ib->idx; // XXX verify
return k;
}
int
sizeorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
if (!ia->k.intkey)
ia->k.intkey = fetch_size(ia->file);
if (!ib->k.intkey)
ib->k.intkey = fetch_size(ib->file);
int k = ia->k.intkey - ib->k.intkey;
if (k == 0)
return ia->idx - ib->idx; // XXX verify
return k;
}
int
mtimeorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
if (!ia->k.intkey)
ia->k.intkey = fetch_mtime(ia->file);
if (!ib->k.intkey)
ib->k.intkey = fetch_mtime(ib->file);
int k = ia->k.intkey - ib->k.intkey;
if (k == 0)
return ia->idx - ib->idx; // XXX verify
return k;
}
int
dateorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
if (!ia->k.intkey)
ia->k.intkey = fetch_date(ia->file);
if (!ib->k.intkey)
ib->k.intkey = fetch_date(ib->file);
int k = ia->k.intkey - ib->k.intkey;
if (k == 0)
return ia->idx - ib->idx; // XXX verify
return k;
}
// taken straight from musl@a593414
int mystrverscmp(const char *l0, const char *r0)
{
const unsigned char *l = (const void *)l0;
const unsigned char *r = (const void *)r0;
size_t i, dp, j;
int z = 1;
/* Find maximal matching prefix and track its maximal digit
* suffix and whether those digits are all zeros. */
for (dp=i=0; l[i]==r[i]; i++) {
int c = l[i];
if (!c) return 0;
if (!isdigit(c)) dp=i+1, z=1;
else if (c!='0') z=0;
}
if (l[dp]!='0' && r[dp]!='0') {
/* If we're not looking at a digit sequence that began
* with a zero, longest digit string is greater. */
for (j=i; isdigit(l[j]); j++)
if (!isdigit(r[j])) return 1;
if (isdigit(r[j])) return -1;
} else if (z && dp<i && (isdigit(l[i]) || isdigit(r[i]))) {
/* Otherwise, if common prefix of digit sequence is
* all zeros, digits order less than non-digits. */
return (unsigned char)(l[i]-'0') - (unsigned char)(r[i]-'0');
}
return l[i] - r[i];
}
int
fileorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
int x = mystrverscmp(a, b);
if (x != 0)
return x;
return ia->idx - ib->idx; // XXX verify
}
int
idxorder(const void *a, const void *b)
{
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
return ia->idx - ib->idx; // XXX verify
}
void
add(char *file)
{
if (idx >= mailalloc) {
mailalloc *= 2;
if (mailalloc < 0)
exit(-1);
mails = realloc(mails, sizeof (struct mail) * mailalloc);
memset(mails+mailalloc/2, 0, sizeof (struct mail) * mailalloc/2);
}
if (!mails)
exit(-1);
mails[idx].file = strdup(file);
mails[idx].idx = idx;
idx++;
}
int
main(int argc, char *argv[])
{
int c, i;
sortorder = idxorder;
while ((c = getopt(argc, argv, "fdsFMSr")) != -1)
switch(c) {
case 'f': sortorder = fromorder; break;
case 'd': sortorder = dateorder; break;
case 's': sortorder = subjorder; break;
case 'F': sortorder = fileorder; break;
case 'M': sortorder = mtimeorder; break;
case 'S': sortorder = sizeorder; break;
case 'r': rflag = !rflag; break;
default:
// XXX usage
exit(1);
}
mails = calloc(sizeof (struct mail), mailalloc);
if (!mails)
exit(-1);
if (argc == optind && isatty(0)) {
char *all[] = { ":" };
blaze822_loop(1, all, add);
} else {
blaze822_loop(argc-optind, argv+optind, add);
}
qsort(mails, idx, sizeof (struct mail), sortorder);
if (rflag)
for (i = idx-1; i >= 0; i--)
printf("%s\n", mails[i].file);
else
for (i = 0; i < idx; i++)
printf("%s\n", mails[i].file);
fprintf(stderr, "%d mails sorted\n", i);
return 0;
}