text: #include <sys/stat.h>
  #include <sys/types.h>
  
  #include <ctype.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <strings.h>
  #include <time.h>
  #include <unistd.h>
  
  #include "blaze822.h"
  
  struct mail {
  	char *file;
  	long idx;
  	char *from;
  	char *subj;
  	time_t date;
  	time_t mtime;
  	off_t size;
  };
  
  struct mail *mails;
  ssize_t mailalloc = 1024;
  
  int idx;
  int rflag;
  
  static int (*sortorders[16])(const void *, const void *);
  int order_idx;
  
  int mystrverscmp(const char *, const char *);
  
  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->subj)
  		ia->subj = fetch_subj(ia->file);
  	if (!ib->subj)
  		ib->subj = fetch_subj(ib->file);
  
  	return strcasecmp(ia->subj, ib->subj);
  }
  
  int
  fromorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	if (!ia->from)
  		ia->from = fetch_from(ia->file);
  	if (!ib->from)
  		ib->from = fetch_from(ib->file);
  
  	return strcasecmp(ia->from, ib->from);
  }
  
  int
  sizeorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	if (!ia->size)
  		ia->size = fetch_size(ia->file);
  	if (!ib->size)
  		ib->size = fetch_size(ib->file);
  
  	if (ia->size > ib->size)
  		return 1;
  	else if (ia->size < ib->size)
  		return -1;
  	return 0;
  }
  
  int
  mtimeorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	if (!ia->mtime)
  		ia->mtime = fetch_mtime(ia->file);
  	if (!ib->mtime)
  		ib->mtime = fetch_mtime(ib->file);
  
  	if (ia->mtime > ib->mtime)
  		return 1;
  	else if (ia->mtime < ib->mtime)
  		return -1;
  	return 0;
  }
  
  int
  dateorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	if (!ia->date)
  		ia->date = fetch_date(ia->file);
  	if (!ib->date)
  		ib->date = fetch_date(ib->file);
  
  	if (ia->date > ib->date)
  		return 1;
  	else if (ia->date < ib->date)
  		return -1;
  	return 0;
  }
  
  int
  fileorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	return mystrverscmp(ia->file, ib->file);
  }
  
  int
  unreadorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	char *fa = strstr(ia->file, ":2,");
  	char *fb = strstr(ib->file, ":2,");
  
  	int unreada = fa ? !strchr(fa, 'S') : 0;
  	int unreadb = fb ? !strchr(fb, 'S') : 0;
  
  	return unreada - unreadb;
  }
  
  int
  flaggedorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	char *fa = strstr(ia->file, ":2,");
  	char *fb = strstr(ib->file, ":2,");
  
  	int unreada = fa ? !!strchr(fa, 'F') : 0;
  	int unreadb = fb ? !!strchr(fb, 'F') : 0;
  
  	return unreadb - unreada;
  }
  
  int
  idxorder(const void *a, const void *b)
  {
  	struct mail *ia = (struct mail *)a;
  	struct mail *ib = (struct mail *)b;
  
  	if (ia->idx > ib->idx)
  		return 1;
  	else if (ia->idx < ib->idx)
  		return -1;
  	else
  		return 0;
  }
  
  void
  add(char *file)
  {
  	if (idx >= mailalloc) {
  		mailalloc *= 2;
  		if (mailalloc < 0)
  			exit(-1);
  		mails = realloc(mails, sizeof (struct mail) * mailalloc);
  		if (!mails)
  			exit(-1);
  		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
  order(const void *a, const void *b)
  {
  	int i, r;
  	for (i = 0, r = 0; i < order_idx; i++) {
  		r = (sortorders[i])(a, b);
  		if (r != 0)
  			return r;
  	}
  	return idxorder(a, b);
  }
  
  void
  addorder(int (*sortorder)(const void *, const void *))
  {
  	if (order_idx < (int)(sizeof sortorders / sizeof sortorders[0]))
  		sortorders[order_idx++] = sortorder;
  }
  
  int
  main(int argc, char *argv[])
  {
  	int c, i;
  
  	while ((c = getopt(argc, argv, "fdsFMSUIr")) != -1)
  		switch (c) {
  		case 'f': addorder(fromorder); break;
  		case 'd': addorder(dateorder); break;
  		case 's': addorder(subjorder); break;
  		case 'F': addorder(fileorder); break;
  		case 'M': addorder(mtimeorder); break;
  		case 'S': addorder(sizeorder); break;
  		case 'U': addorder(unreadorder); break;
  		case 'I': addorder(flaggedorder); break;
  		case 'r': rflag = !rflag; break;
  		default:
  			fprintf(stderr,
  			    "Usage: msort [-r] [-fdsFMSUI] [msgs...]\n");
  			exit(1);
  		}
  
  
  	mails = calloc(sizeof (struct mail), mailalloc);
  	if (!mails)
  		exit(-1);
  
  	if (argc == optind && isatty(0))
  		blaze822_loop1(":", add);
  	else
  		blaze822_loop(argc-optind, argv+optind, add);
  
  	qsort(mails, idx, sizeof (struct mail), order);
  
  	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);
  
  	return 0;
  }
  