diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2003-03-10 13:44:37 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2003-03-10 13:44:37 +0000 |
commit | f4e4082ae1da285ec7abe072a945d606f2f79056 (patch) | |
tree | cd04a155e5622816f28a4f9c9ef3a9fb20f0f46b | |
parent | 1157f8276de4740fdfeaf9bff2e0f4ce8d67dc82 (diff) | |
download | mailutils-f4e4082ae1da285ec7abe072a945d606f2f79056.tar.gz mailutils-f4e4082ae1da285ec7abe072a945d606f2f79056.tar.bz2 |
New file. Source for the sortm utility.
-rw-r--r-- | mh/sortm.c | 563 |
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; +} |