summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2003-03-10 13:44:37 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2003-03-10 13:44:37 +0000
commitf4e4082ae1da285ec7abe072a945d606f2f79056 (patch)
treecd04a155e5622816f28a4f9c9ef3a9fb20f0f46b
parent1157f8276de4740fdfeaf9bff2e0f4ce8d67dc82 (diff)
downloadmailutils-f4e4082ae1da285ec7abe072a945d606f2f79056.tar.gz
mailutils-f4e4082ae1da285ec7abe072a945d606f2f79056.tar.bz2
New file. Source for the sortm utility.
-rw-r--r--mh/sortm.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/mh/sortm.c b/mh/sortm.c
new file mode 100644
index 000000000..54265e249
--- /dev/null
+++ b/mh/sortm.c
@@ -0,0 +1,563 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2003 Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Mailutils is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* MH sortm command */
+
+#include <mh.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+
+const char *argp_program_version = "sortm (" PACKAGE_STRING ")";
+static char doc[] = N_("GNU MH sortm\v"
+"Use -help to obtain the list of traditional MH options.");
+static char args_doc[] = N_("[msgs]");
+
+#define ARG_QUICKSORT 1024
+#define ARG_SHELL 1025
+
+/* GNU options */
+static struct argp_option options[] = {
+ {"folder", ARG_FOLDER, N_("FOLDER"), 0,
+ N_("Specify folder to operate upon")},
+
+ {N_("Setting sort keys:"), 0, NULL, OPTION_DOC, NULL, 0},
+ {"datefield", ARG_DATEFIELD, N_("STRING"), 0,
+ N_("Sort on the date field (default `Date:')"), 10},
+ {"nodatefield", ARG_NODATEFIELD, NULL, 0,
+ N_("Undo the effect of the last --datefield option"), 10},
+ {"limit", ARG_LIMIT, N_("DAYS"), 0,
+ N_("Consider two datefields equal if their difference lies within the given nuber of DAYS."), 11},
+ {"nolimit", ARG_NOLIMIT, NULL, 0,
+ N_("Undo the effect of the last --limit option"), 11},
+ {"textfield", ARG_TEXTFIELD, N_("STRING"), 15,
+ N_("Sort on the text field"), 1},
+ {"notextfield", ARG_NOTEXTFIELD, NULL, 0,
+ N_("Undo the effect of the last --textfield option"), 15},
+ {"numfield", ARG_NUMFIELD, N_("STRING"), 0,
+ N_("Sort on the numeric field"), 16},
+
+ {N_("Actions:"), 0, NULL, OPTION_DOC, NULL, 16},
+ {"reorder", ARG_REORDER, 0, 0,
+ N_("Reorder the messages (default)"), 20 },
+ {"dry-run", ARG_DRY_RUN, 0, 0,
+ N_("Do not do anything, only show what would have been done"), 20 },
+ {"list", ARG_LIST, 0, 0,
+ N_("List the sorted messages"), 20 },
+ {"form", ARG_FORM, N_("FILE"), 0,
+ N_("Read format from given file"), 23},
+ {"format", ARG_FORMAT, N_("FORMAT"), 0,
+ N_("Use this format string"), 23},
+
+ {"verbose", ARG_VERBOSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
+ N_("Verbosely list executed actions"), 30 },
+ {"noverbose", ARG_NOVERBOSE, NULL, OPTION_HIDDEN, "" },
+
+ {N_("Select sort algorithm:"), 0, NULL, OPTION_DOC, NULL, 30},
+
+ {"shell", ARG_SHELL, 0, 0,
+ N_("Use shell algorithm"), 40 },
+ {"quicksort", ARG_QUICKSORT, 0, 0,
+ N_("Use quicksort algorithm (default)"), 40 },
+ { NULL },
+};
+
+/* Traditional MH options */
+struct mh_option mh_option[] = {
+ {"datefield", 1, 0, "field" },
+ {"nodatefield", 3, 0, 0 },
+ {"textfield", 1, 0, "field" },
+ {"notextfield", 3, 0, 0 },
+ {"limit", 1, 0, "days" },
+ {"nolimit", 3, 0, 0 },
+ {"verbose", 1, MH_OPT_BOOL, NULL},
+ { NULL },
+};
+
+static int limit;
+static int verbose;
+static mailbox_t mbox;
+static const char *mbox_path;
+static mh_msgset_t msgset;
+
+#define ACTION_REORDER 0
+#define ACTION_DRY_RUN 1
+#define ACTION_LIST 2
+
+static int algorithm = ARG_QUICKSORT;
+static int action = ACTION_REORDER;
+static char *format_str = mh_list_format;
+static mh_format_t format;
+
+typedef int (*compfun) __PMT((void *, void *));
+static void addop __P((char *field, compfun comp));
+static void remop __P((compfun comp));
+static int comp_text __P((void *a, void *b));
+static int comp_date __P((void *a, void *b));
+static int comp_number __P((void *a, void *b));
+
+static int
+opt_handler (int key, char *arg, void *unused, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARG_FOLDER:
+ current_folder = arg;
+ break;
+
+ case ARG_DATEFIELD:
+ addop (arg, comp_date);
+ break;
+
+ case ARG_NUMFIELD:
+ addop (arg, comp_number);
+ break;
+
+ case ARG_NODATEFIELD:
+ remop (comp_date);
+ break;
+
+ case ARG_TEXTFIELD:
+ addop (arg, comp_text);
+ break;
+
+ case ARG_NOTEXTFIELD:
+ remop (comp_text);
+ break;
+
+ case ARG_LIMIT:
+ limit = strtoul (arg, NULL, 0);
+ break;
+
+ case ARG_NOLIMIT:
+ limit = -1;
+ break;
+
+ case ARG_VERBOSE:
+ if (!arg || isalpha (arg[0]))
+ verbose = is_true (arg);
+ else
+ verbose = arg[0] - '0';
+ break;
+
+ case ARG_NOVERBOSE:
+ verbose = 0;
+ break;
+
+ case ARG_FORM:
+ mh_read_formfile (arg, &format_str);
+ break;
+
+ case ARG_FORMAT:
+ format_str = arg;
+ break;
+
+ case ARG_REORDER:
+ action = ACTION_REORDER;
+ break;
+
+ case ARG_LIST:
+ action = ACTION_LIST;
+ break;
+
+ case ARG_DRY_RUN:
+ action = ACTION_DRY_RUN;
+ if (!verbose)
+ verbose = 1;
+ break;
+
+ case ARG_SHELL:
+ case ARG_QUICKSORT:
+ algorithm = key;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+
+/* *********************** Comparison functions **************************** */
+struct comp_op {
+ char *field;
+ compfun comp;
+};
+
+static list_t oplist;
+
+static void
+addop (char *field, compfun comp)
+{
+ struct comp_op *op = xmalloc (sizeof (*op));
+
+ if (!oplist && list_create (&oplist))
+ {
+ mh_error (_("can't create operation list"));
+ exit (1);
+ }
+ op->field = field;
+ op->comp = comp;
+ list_append (oplist, op);
+}
+
+struct rem_data {
+ struct comp_op *op;
+ compfun comp;
+};
+
+static int
+rem_action (void *item, void *data)
+{
+ struct comp_op *op = item;
+ struct rem_data *d = data;
+ if (d->comp == op->comp)
+ d->op = op;
+ return 0;
+}
+
+static void
+remop (compfun comp)
+{
+ struct rem_data d;
+ d.comp = comp;
+ d.op = NULL;
+ list_do (oplist, rem_action, &d);
+ list_remove (oplist, d.op);
+ free (d.op);
+}
+
+struct comp_data {
+ int r;
+ message_t m[2];
+};
+
+static int
+compare_action (void *item, void *data)
+{
+ struct comp_op *op = item;
+ struct comp_data *dp = data;
+ char *a, *ap, *b, *bp;
+ header_t h;
+
+ if (message_get_header (dp->m[0], &h)
+ || header_aget_value (h, op->field, &a))
+ return 0;
+
+ if (message_get_header (dp->m[1], &h)
+ || header_aget_value (h, op->field, &b))
+ {
+ free (a);
+ return 0;
+ }
+
+ ap = a;
+ bp = b;
+ if (strcasecmp (op->field, MU_HEADER_SUBJECT) == 0)
+ {
+ if (strncasecmp (ap, "re:", 3) == 0)
+ ap += 3;
+ if (strncasecmp (b, "re:", 3) == 0)
+ bp += 3;
+ }
+
+ dp->r = op->comp (ap, bp);
+ free (a);
+ free (b);
+
+ return dp->r; /* go on until the difference is found */
+}
+
+static int
+compare_messages (message_t a, message_t b)
+{
+ struct comp_data d;
+
+ d.r = 0;
+ d.m[0] = a;
+ d.m[1] = b;
+ list_do (oplist, compare_action, &d);
+ if (verbose > 1)
+ fprintf (stderr, "%d\n", d.r);
+ return d.r;
+}
+
+static int
+comp_text (void *a, void *b)
+{
+ return strcasecmp (a, b);
+}
+
+static int
+comp_number (void *a, void *b)
+{
+ long na, nb;
+
+ na = strtol (a, NULL, 0);
+ nb = strtol (b, NULL, 0);
+ if (na > nb)
+ return 1;
+ else if (na < nb)
+ return -1;
+ return 0;
+}
+
+/*FIXME: Also used in imap4d*/
+static int
+_parse_822_date (char *date, time_t * timep)
+{
+ struct tm tm;
+ mu_timezone tz;
+ const char *p = date;
+
+ if (parse822_date_time (&p, date + strlen (date), &tm, &tz) == 0)
+ {
+ *timep = mu_tm2time (&tm, &tz);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+comp_date (void *a, void *b)
+{
+ time_t ta, tb;
+
+ if (_parse_822_date (a, &ta) || _parse_822_date (b, &tb))
+ return 0;
+
+ if (ta < tb)
+ {
+ if (limit && tb - ta <= limit)
+ return 0;
+ return -1;
+ }
+ else if (ta > tb)
+ {
+ if (limit && ta - tb <= limit)
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+
+/* *********************** Sorting routines ***************************** */
+
+static int
+comp0 (size_t na, size_t nb)
+{
+ message_t a, b;
+
+ if (mailbox_get_message (mbox, na, &a)
+ || mailbox_get_message (mbox, nb, &b))
+ return 0;
+ if (verbose > 1)
+ fprintf (stderr,
+ _("comparing messages %lu and %lu: "),
+ (unsigned long) na,
+ (unsigned long) nb);
+ return compare_messages (a, b);
+}
+
+int
+comp (const void *a, const void *b)
+{
+ return comp0 (* (size_t*) a, * (size_t*) b);
+}
+
+
+/* ****************************** Shell sort ****************************** */
+#define prevdst(h) ((h)-1)/3
+
+static int
+startdst (unsigned count, int *num)
+{
+ int i, h;
+
+ for (i = h = 1; 9*h + 4 < count; i++, h = 3*h+1)
+ ;
+ *num = i;
+ return h;
+}
+
+void
+shell_sort ()
+{
+ int h, s, i, j;
+ size_t hold;
+
+ for (h = startdst (msgset.count, &s); s > 0; s--, h = prevdst (h))
+ {
+ if (verbose > 1)
+ fprintf (stderr, _("distance %d\n"), h);
+ for (j = h; j < msgset.count; j++)
+ {
+ hold = msgset.list[j];
+ for (i = j - h;
+ i >= 0 && comp0 (hold, msgset.list[i]) < 0; i -= h)
+ msgset.list[i + h] = msgset.list[i];
+ msgset.list[i + h] = hold;
+ }
+ }
+}
+
+
+/* ****************************** Actions ********************************* */
+
+void
+list_message (size_t num)
+{
+ message_t msg = NULL;
+ char *buffer;
+ mailbox_get_message (mbox, num, &msg);
+ mh_format (&format, msg, num, 76, &buffer);
+ printf ("%s\n", buffer);
+ free (buffer);
+}
+
+void
+swap_message (size_t a, size_t b)
+{
+ char *path_a, *path_b;
+ char *tmp;
+
+ asprintf (&path_a, "%s/%lu", mbox_path, (unsigned long) a);
+ asprintf (&path_b, "%s/%lu", mbox_path, (unsigned long) b);
+ tmp = mu_tempname (mbox_path);
+ rename (path_a, tmp);
+ unlink (path_a);
+ rename (path_b, path_a);
+ unlink (path_b);
+ rename (tmp, path_b);
+ free (tmp);
+}
+
+void
+transpose(size_t i, size_t n)
+{
+ size_t j;
+
+ for (j = i+1; j < msgset.count; j++)
+ if (msgset.list[j] == n)
+ {
+ size_t t = msgset.list[i];
+ msgset.list[i] = msgset.list[j];
+ msgset.list[j] = t;
+ break;
+ }
+}
+
+static int got_signal;
+
+RETSIGTYPE
+sighandler (int sig)
+{
+ got_signal = 1;
+}
+
+void
+sort ()
+{
+ size_t *oldlist, i;
+ oldlist = xmalloc (msgset.count * sizeof (*oldlist));
+ memcpy (oldlist, msgset.list, msgset.count * sizeof (*oldlist));
+
+ switch (algorithm)
+ {
+ case ARG_QUICKSORT:
+ qsort(msgset.list, msgset.count, sizeof(msgset.list[0]),
+ comp);
+ break;
+
+ case ARG_SHELL:
+ shell_sort();
+ break;
+ }
+
+ switch (action)
+ {
+ case ACTION_LIST:
+ for (i = 0; i < msgset.count; i++)
+ list_message (msgset.list[i]);
+ break;
+
+ default:
+ /* Install signal handlers */
+ signal (SIGINT, sighandler);
+ signal (SIGQUIT, sighandler);
+ signal (SIGTERM, sighandler);
+
+ if (verbose)
+ fprintf (stderr, _("Transpositions:\n"));
+ for (i = 0, got_signal = 0; !got_signal && i < msgset.count; i++)
+ {
+ if (msgset.list[i] != oldlist[i])
+ {
+ size_t old_num, new_num;
+ message_t msg;
+
+ mailbox_get_message (mbox, oldlist[i], &msg);
+ mh_message_number (msg, &old_num);
+ mailbox_get_message (mbox, msgset.list[i], &msg);
+ mh_message_number (msg, &new_num);
+ transpose (i, oldlist[i]);
+ if (verbose)
+ fprintf (stderr, "{%lu, %lu}\n",
+ (unsigned long) old_num,
+ (unsigned long) new_num);
+ if (action == ACTION_REORDER)
+ swap_message (old_num, new_num);
+ }
+ }
+ }
+}
+
+
+/* Main */
+
+int
+main (int argc, char **argv)
+{
+ int index;
+ url_t url;
+
+ mu_init_nls ();
+ mh_argp_parse (argc, argv, 0, options, mh_option,
+ args_doc, doc, opt_handler, NULL, &index);
+ if (!oplist)
+ addop ("date", comp_date);
+
+ if (action == ACTION_LIST && mh_format_parse (format_str, &format))
+ {
+ mh_error (_("Bad format string"));
+ exit (1);
+ }
+
+ mbox = mh_open_folder (current_folder, 0);
+ mailbox_get_url (mbox, &url);
+ mbox_path = url_to_string (url);
+ if (memcmp (mbox_path, "mh:", 3) == 0)
+ mbox_path += 3;
+
+ argc -= index;
+ argv += index;
+
+ mh_msgset_parse (mbox, &msgset, argc, argv, "all");
+ sort (mbox, msgset);
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.