]> mj.ucw.cz Git - checkmail.git/blobdiff - cm.c
Better rules for coloring of flagged messages.
[checkmail.git] / cm.c
diff --git a/cm.c b/cm.c
index 09dce992e942ea22dbc6bf48e1ef663ac5b833dd..a3bba1853305c46e21b595475157a6ec37ff5b0a 100644 (file)
--- a/cm.c
+++ b/cm.c
@@ -1,15 +1,17 @@
 /*
  *     Incoming Mail Checker
  *
- *     (c) 2005 Martin Mares <mj@ucw.cz>
+ *     (c) 2005--2007 Martin Mares <mj@ucw.cz>
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <ctype.h>
 #include <getopt.h>
 #include <fcntl.h>
 #include <glob.h>
+#include <fnmatch.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <pwd.h>
 
 #include "util.h"
 #include "clists.h"
+#include "charset.h"
 
 static int check_interval = 30;
 static int force_refresh;
-static int lock_mboxes;
+static int allow_bells = 1;
+static int minimum_priority;
 static time_t last_scan_time;
 static char *run_cmd = "mutt -f %s";
 
+struct options {
+  int priority;
+  int hide;
+  int hide_if_empty;
+  int highlight;
+  int beep;
+  int snippets;
+  int show_flagged;
+  int sender_personal;
+  int sender_mbox;
+};
+
+struct option_node {
+  cnode n;
+  struct options o;
+  char pattern[1];
+};
+
+struct pattern_node {
+  cnode n;
+  char *name;
+  char pattern[1];
+};
+
+static clist options, patterns;
+static struct options global_options = {
+  .sender_personal = 1
+};
+
 struct mbox {
   cnode n;
+  struct options o;
+  char *name;
+  char *path;
   int index;
-  int hilited;
   int scanning;
   int seen;
   time_t last_time;
   int last_size, last_pos;
-  char *name;
-  char *path;
-  int total, new;
-  int last_total, last_new;
+  int total, new, flagged;
+  int last_total, last_new, last_flagged;
+  int last_beep_new;
   int force_refresh;
+  int snippet_is_new;
+  char snippet[256];
 };
 
-static clist mboxes, hilites, patterns;
+static clist mboxes;
 static struct mbox **mbox_array;
-static int num_mboxes;
+static int num_mboxes, mbox_array_size;
 
 static void redraw_line(int i);
-static void redraw_all(void);
+static void rethink_display(void);
+
+static void
+add_pattern(char *patt)
+{
+  struct pattern_node *n = xmalloc(sizeof(*n) + strlen(patt));
+  strcpy(n->pattern, patt);
+  if (patt = strchr(n->pattern, '='))
+    {
+      *patt++ = 0;
+      n->name = patt;
+    }
+  else
+    n->name = NULL;
+  clist_add_tail(&patterns, &n->n);
+}
+
+static void
+add_inbox(void)
+{
+  struct passwd *p = getpwuid(getuid());
+  if (!p)
+    die("You don't exist, go away!");
+  char buf[sizeof("/var/mail/") + strlen(p->pw_name) + 7];
+  sprintf(buf, "/var/mail/%s=INBOX", p->pw_name);
+  add_pattern(buf);
+}
+
+static void
+init_options(struct options *o)
+{
+  o->priority = -1;
+  o->hide = -1;
+  o->hide_if_empty = -1;
+  o->beep = -1;
+  o->highlight = -1;
+  o->snippets = -1;
+  o->show_flagged = -1;
+  o->sender_personal = -1;
+  o->sender_mbox = -1;
+}
+
+static void
+setup_options(struct mbox *b)
+{
+  b->o = global_options;
+  CLIST_FOR_EACH(struct option_node *, n, options)
+    if (!fnmatch(n->pattern, b->name, 0))
+      {
+       debug("\tApplied options %s\n", n->pattern);
+#define MERGE(f) if (n->o.f >= 0) b->o.f = n->o.f
+       MERGE(priority);
+       MERGE(hide);
+       MERGE(hide_if_empty);
+       MERGE(highlight);
+       MERGE(beep);
+       MERGE(snippets);
+       MERGE(show_flagged);
+       MERGE(sender_personal);
+       MERGE(sender_mbox);
+      }
+}
 
 static char *
 mbox_name(char *path)
@@ -93,15 +190,47 @@ find_mbox(clist *l, char *path)
   return NULL;
 }
 
+static inline int
+mbox_active_p(struct mbox *b)
+{
+  if (b->o.priority < minimum_priority)
+    return 0;
+  if (b->o.hide)
+    return 0;
+  return 1;
+}
+
+static inline int
+mbox_visible_p(struct mbox *b)
+{
+  if (!mbox_active_p(b))
+    return 0;
+  if (b->scanning < 0)
+    return 1;
+  if (b->o.hide_if_empty && !b->total)
+    return 0;
+  return 1;
+}
+
 static void
-add_inbox(clist *l)
+prepare_snippet(struct mbox *b, char *sender, char *subject)
 {
-  struct passwd *p = getpwuid(getuid());
-  if (!p)
-    die("You don't exist, go away!");
-  char buf[sizeof("/var/mail/") + strlen(p->pw_name) + 1];
-  sprintf(buf, "/var/mail/%s", p->pw_name);
-  add_mbox(l, buf, "INBOX");
+  while (*sender == ' ' || *sender == '\t')
+    sender++;
+  while (*subject == ' ' || *subject == '\t')
+    subject++;
+
+  char *pos = b->snippet;
+  char *term = b->snippet + sizeof(b->snippet) - 1;
+  if (sender[0] && (b->o.sender_mbox || b->o.sender_personal))
+    {
+      add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
+      add_snippet(&pos, term, ": ");
+    }
+  if (subject[0])
+    add_subject_snippet(&pos, term, subject);
+  else
+    add_snippet(&pos, term, "No subject");
 }
 
 static int mb_fd, mb_pos;
@@ -114,7 +243,7 @@ mb_reset(int pos)
   mb_pos = pos;
 }
 
-static int
+static void
 mb_seek(uns pos)
 {
   lseek(mb_fd, pos, SEEK_SET);
@@ -151,6 +280,13 @@ mb_get(void)
   return (mb_cc < mb_end) ? *mb_cc++ : mb_ll_get();
 }
 
+static void
+mb_unget(int c)
+{
+  if (c >= 0)
+    mb_cc--;
+}
+
 static int
 mb_check(const char *p, int len)
 {
@@ -165,13 +301,13 @@ mb_check(const char *p, int len)
 static void
 scan_mbox(struct mbox *b, struct stat *st)
 {
-  char buf[1024];
+  char buf[1024], sender[1024], subject[1024];
   int c;
   const char from[] = "\nFrom ";
 
   if (!st->st_size)
     {
-      b->total = b->new = 0;
+      b->total = b->new = b->flagged = 0;
       b->last_pos = 0;
       return;
     }
@@ -182,7 +318,7 @@ scan_mbox(struct mbox *b, struct stat *st)
   if (mb_fd < 0)
     {
       debug("[open failed: %m] ");
-      b->total = b->new = -1;
+      b->total = b->new = b->flagged = -1;
       return;
     }
   mb_reset(0);
@@ -207,16 +343,18 @@ scan_mbox(struct mbox *b, struct stat *st)
       if (!mb_check(from+1, 5))
        {
          debug("[inconsistent] ");
-         b->total = b->new = -1;
+         b->total = b->new = b->flagged = -1;
          goto done;
        }
-      b->total = b->new = 0;
-      b->last_total = b->last_new = 0;
+      b->total = b->new = b->flagged = 0;
+      b->last_total = b->last_new = b->last_flagged = 0;
+      b->snippet_is_new = 0;
     }
   else
     {
       b->total = b->last_total;
       b->new = b->last_new;
+      b->flagged = b->last_flagged;
     }
 
   for(;;)
@@ -226,10 +364,14 @@ scan_mbox(struct mbox *b, struct stat *st)
        b->last_pos--;          // last_pos should be the previous \n character
       b->last_total = b->total;
       b->last_new = b->new;
+      b->last_flagged = b->flagged;
       while ((c = mb_get()) >= 0 && c != '\n')
        ;
 
       int new = 1;
+      int flagged = 0;
+      sender[0] = 0;
+      subject[0] = 0;
       for (;;)
        {
          uns i = 0;
@@ -242,7 +384,19 @@ scan_mbox(struct mbox *b, struct stat *st)
                  goto done;
                }
              if (c == '\n')
-               break;
+               {
+                 int fold = -1;
+                 do
+                   {
+                     fold++;
+                     c = mb_get();
+                   }
+                 while (c == ' ' || c == '\t');
+                 mb_unget(c);
+                 if (!fold)
+                   break;
+                 c = ' ';
+               }
              if (c == '\r')
                continue;
              if (i < sizeof(buf) - 1)
@@ -253,11 +407,24 @@ scan_mbox(struct mbox *b, struct stat *st)
            break;
          if (!strncasecmp(buf, "Status:", 7))
            new = 0;
+         else if (!strncasecmp(buf, "X-Status:", 9) && strchr(buf+9, 'F'))
+           flagged = 1;
+         else if (!strncasecmp(buf, "From:", 5))
+           strcpy(sender, buf+5);
+         else if (!strncasecmp(buf, "Subject:", 8))
+           strcpy(subject, buf+8);
        }
 
       b->total++;
       if (new)
        b->new++;
+      if (flagged)
+       b->flagged++;
+      if (new || (flagged && !b->snippet_is_new))
+       {
+         b->snippet_is_new = new;
+         prepare_snippet(b, sender, subject);
+       }
 
       int ct = 1;
       while (from[ct])
@@ -279,13 +446,13 @@ scan(void)
 {
   debug("Searching for mailboxes...\n");
   last_scan_time = time(NULL);
-  int changed = 0;
-  CLIST_FOR_EACH(struct mbox *, p, patterns)
+  CLIST_FOR_EACH(struct pattern_node *, p, patterns)
     {
+      debug("Trying pattern %s (name %s)\n", p->pattern, p->name);
       glob_t g;
-      int err = glob(p->path, GLOB_ERR | GLOB_NOSORT | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &g);
+      int err = glob(p->pattern, GLOB_ERR | GLOB_NOSORT | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &g);
       if (err && err != GLOB_NOMATCH)
-       die("Failed to glob %s: %m", p->path);
+       die("Failed to glob %s: %m", p->pattern);
       for (uns i=0; i<g.gl_pathc; i++)
        {
          char *name = g.gl_pathv[i];
@@ -293,59 +460,44 @@ scan(void)
          if (!b)
            {
              b = add_mbox(&mboxes, name, (p->name ? p->name : mbox_name(name)));
-             if (find_mbox(&hilites, b->name))
-               b->hilited = 1;
-             debug("Discovered mailbox %s (%s) hilited=%d\n", b->name, b->path, b->hilited);
+             debug("Discovered mailbox %s (%s)\n", b->name, b->path);
+             setup_options(b);
              b->scanning = -1;
-             changed = 1;
            }
          b->seen = 1;
        }
       globfree(&g);
     }
 
-  num_mboxes = 0;
   struct mbox *tmp;
   CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
     {
       if (b->seen)
-       {
-         num_mboxes++;
-         b->seen = 0;
-       }
+       b->seen = 0;
       else
        {
          debug("Lost mailbox %s\n", b->name);
-         changed = 1;
          del_mbox(b);
        }
     }
 
-  if (changed)
-    {
-      debug("Reallocating mailbox array...\n");
-      free(mbox_array);
-      mbox_array = xmalloc(sizeof(struct mbox *) * (num_mboxes+1));
-      int i = 0;
-      CLIST_FOR_EACH(struct mbox *, b, mboxes)
-       {
-         b->index = i;
-         mbox_array[i++] = b;
-       }
-      redraw_all();
-      refresh();
-    }
+  rethink_display();
 
   debug("Scanning mailboxes...\n");
   CLIST_FOR_EACH(struct mbox *, b, mboxes)
     {
       struct stat st;
       debug("%s: ", b->name);
+      if (!mbox_active_p(b))
+       {
+         debug("inactive\n");
+         continue;
+       }
       if (force_refresh)
        b->force_refresh = 1;
       if (stat(b->path, &st) < 0)
        {
-         b->total = b->new = -1;
+         b->total = b->new = b->flagged = -1;
          debug("%m\n");
        }
       else if (!b->last_time || st.st_mtime != b->last_time || st.st_size != b->last_size || b->force_refresh)
@@ -357,7 +509,7 @@ scan(void)
          scan_mbox(b, &st);
          b->last_time = st.st_mtime;
          b->last_size = st.st_size;
-         debug("%d %d (stopped at %d of %d)\n", b->total, b->new, b->last_pos, b->last_size);
+         debug("%d %d %d (stopped at %d of %d)\n", b->total, b->new, b->flagged, b->last_pos, b->last_size);
 
          b->scanning = 0;
          redraw_line(b->index);
@@ -371,6 +523,7 @@ scan(void)
 
   debug("Scan finished\n");
   last_scan_time = time(NULL);
+  rethink_display();
 }
 
 static int cursor_at, cursor_max;
@@ -379,6 +532,7 @@ enum {
   M_IDLE,
   M_SCAN,
   M_NEW,
+  M_FLAG,
   M_BAD,
   M_MAX
 };
@@ -392,7 +546,7 @@ redraw_line(int i)
     {
       struct mbox *b = mbox_array[i];
       int cc = (cursor_at == i);
-      int hi = b->hilited;
+      int hi = b->o.highlight;
 
       attrset(attrs[cc][hi][M_IDLE]);
       if (cc)
@@ -401,6 +555,8 @@ redraw_line(int i)
        printw("  ");
       if (b->new)
        attrset(attrs[cc][hi][M_NEW]);
+      else if (b->flagged)
+       attrset(attrs[cc][hi][M_FLAG]);
       printw("%-20s ", b->name);
       if (b->scanning < 0)
        ;
@@ -418,6 +574,7 @@ redraw_line(int i)
        {
          attrset(attrs[cc][hi][M_IDLE]);
          printw("%6d ", b->total);
+         int snip = 0;
          if (b->new)
            {
              attrset(attrs[cc][hi][M_NEW]);
@@ -427,15 +584,33 @@ redraw_line(int i)
              if (age < 0)
                age = 0;
              if (age < 3600)
-               printw("%2d min", age/60);
+               printw("%2d min  ", age/60);
              else if (age < 86400)
-               printw("%2d hrs", age/3600);
+               printw("%2d hrs  ", age/3600);
+             else
+               printw("        ");
+             snip = 1;
+           }
+         else if (b->flagged)
+           {
+             attrset(attrs[cc][hi][M_FLAG]);
+             printw("%6d  ", b->flagged);
+             attrset(attrs[cc][hi][M_IDLE]);
+             printw("        ");
+             attrset(attrs[cc][0][M_FLAG]);    /* We avoid the highlight intentionally */
+             snip = b->o.show_flagged;
+           }
+         if (snip && b->o.snippets && b->snippet[0])
+           {
+             int xx, yy;
+             getyx(stdscr, yy, xx);
+             int remains = COLS-1-xx;
+             if (remains > 2)
+               printw("%-.*s", remains, b->snippet);
            }
        }
-      attrset(attrs[cc][hi][M_IDLE]);
     }
-  else
-    attrset(attrs[0][0][M_IDLE]);
+  attrset(attrs[0][0][M_IDLE]);
   clrtoeol();
 }
 
@@ -456,11 +631,50 @@ redraw_all(void)
   if (!cursor_max)
     {
       printw("(no mailboxes found)");
+      clrtoeol();
       move(1, 0);
     }
   clrtobot();
 }
 
+static void
+rethink_display(void)
+{
+  int i = 0;
+  int changed = 0;
+  int beeeep = 0;
+  CLIST_FOR_EACH(struct mbox *, b, mboxes)
+    if (mbox_visible_p(b))
+      {
+       b->index = i;
+       if (i >= num_mboxes || mbox_array[i] != b)
+         {
+           changed = 1;
+           if (i >= mbox_array_size)
+             {
+               mbox_array_size = (mbox_array_size ? 2*mbox_array_size : 16);
+               mbox_array = xrealloc(mbox_array, sizeof(struct mbox *) * mbox_array_size);
+             }
+           mbox_array[i] = b;
+         }
+       if (b->o.beep && b->new > b->last_beep_new)
+         beeeep = 1;
+       b->last_beep_new = b->new;
+       i++;
+      }
+  if (i != num_mboxes)
+    changed = 1;
+  num_mboxes = i;
+
+  if (changed)
+    {
+      redraw_all();
+      refresh();
+    }
+  if (beeeep && allow_bells)
+    beep();
+}
+
 static void
 term_init(void)
 {
@@ -473,8 +687,8 @@ term_init(void)
   curs_set(0);
 
   static const int attrs_mono[2][M_MAX] = {
-    [0] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_BOLD, [M_BAD] = A_DIM },
-    [1] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_REVERSE | A_BOLD, [M_BAD] = A_DIM },
+    [0] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_BOLD, [M_FLAG] = 0, [M_BAD] = A_DIM },
+    [1] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_REVERSE | A_BOLD, [M_FLAG] = A_REVERSE, [M_BAD] = A_DIM },
   };
   for (int i=0; i<2; i++)
     for (int j=0; j<M_MAX; j++)
@@ -493,16 +707,17 @@ term_init(void)
          init_pair(3, COLOR_WHITE, COLOR_BLUE);
          init_pair(4, COLOR_YELLOW, COLOR_BLUE);
          init_pair(5, COLOR_RED, COLOR_BLUE);
+         init_pair(6, COLOR_GREEN, COLOR_BLACK);
+         init_pair(7, COLOR_GREEN, COLOR_BLUE);
          static const int attrs_color[2][2][M_MAX] = {
-           [0][0] = { [M_IDLE] = 0, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1), [M_BAD] = COLOR_PAIR(2) },
-           [0][1] = { [M_IDLE] = A_BOLD, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1) | A_BOLD, [M_BAD] = COLOR_PAIR(2) | A_BOLD },
-           [1][0] = { [M_IDLE] = COLOR_PAIR(3), [M_SCAN] = COLOR_PAIR(4), [M_NEW] = COLOR_PAIR(4), [M_BAD] = COLOR_PAIR(5) },
-           [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD, [M_SCAN] = COLOR_PAIR(4), [M_NEW] = COLOR_PAIR(4) | A_BOLD, [M_BAD] = COLOR_PAIR(5) | A_BOLD },
+           [0][0] = { [M_IDLE] = 0, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1), [M_FLAG] = COLOR_PAIR(6), [M_BAD] = COLOR_PAIR(2) },
+           [0][1] = { [M_IDLE] = A_BOLD, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1) | A_BOLD, [M_FLAG] = COLOR_PAIR(6) | A_BOLD, [M_BAD] = COLOR_PAIR(2) | A_BOLD },
+           [1][0] = { [M_IDLE] = COLOR_PAIR(3), [M_SCAN] = COLOR_PAIR(4), [M_NEW] = COLOR_PAIR(4), [M_FLAG] = COLOR_PAIR(7), [M_BAD] = COLOR_PAIR(5) },
+           [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD, [M_SCAN] = COLOR_PAIR(4), [M_NEW] = COLOR_PAIR(4) | A_BOLD, [M_FLAG] = COLOR_PAIR(7) | A_BOLD, [M_BAD] = COLOR_PAIR(5) | A_BOLD },
          };
          memcpy(attrs, attrs_color, sizeof(attrs));
        }
     }
-  clear();
 }
 
 static void
@@ -512,14 +727,21 @@ term_cleanup(void)
 }
 
 static void
-scan_and_redraw(void)
+print_status(char *status)
 {
   move(LINES-1, 0);
-  printw("Busy...");
+  if (status)
+    printw("%s", status);
+  clrtoeol();
   refresh();
+}
+
+static void
+scan_and_redraw(void)
+{
+  print_status("Busy...");
   scan();
-  redraw_all();
-  refresh();
+  print_status(NULL);
 }
 
 static void
@@ -535,53 +757,131 @@ move_cursor(int i)
 }
 
 static void
-next_active(int since)
+next_active(int since, int step)
 {
   if (!cursor_max)
     return;
-  since %= cursor_max;
-  for (int pass=1; pass >= 0; pass--)
+  since = (since+cursor_max) % cursor_max;
+  step = (step+cursor_max) % cursor_max;
+  int besti = -1;
+  int bestp = -1;
+  int i = since;
+  do
     {
-      int i = since;
-      do {
-       if (mbox_array[i]->hilited >= pass && mbox_array[i]->new)
-         {
-           move_cursor(i);
-           return;
-         }
-       i = (i+1) % cursor_max;
-      } while (i != since);
+      struct mbox *b = mbox_array[i];
+      if (b->new && b->o.priority > bestp)
+       {
+         besti = i;
+         bestp = b->o.priority;
+       }
+      i = (i+step) % cursor_max;
     }
+  while (i != since);
+  if (besti >= 0)
+    move_cursor(besti);
 }
 
+#define STR2(c) #c
+#define STR(c) STR2(c)
+
 static void NONRET
 usage(void)
 {
-  fprintf(stderr, "Usage: cm [<options>] <mbox-patterns>\n\
+  fprintf(stderr, "Usage: cm [<options>] [<mbox-pattern> | <mbox>[=<name>]] ...\n\
 \n\
 Options:\n\
 -c <interval>\t\tScan mailboxes every <interval> seconds (default is 30)\n\
 -d\t\t\tLog debug messages to stderr\n\
--h <mbox>\t\tHighlight and prefer the specified mailbox\n\
 -i\t\t\tInclude user's INBOX\n\
--l\t\t\tLock mailboxes when scanning\n\
 -m <cmd>\t\tCommand to run on the selected mailbox, %%s gets replaced by mailbox path\n\
+-o <pattern>=<opts>\tSet mailbox options\n\
+-o <opts>\t\tSet default options for all mailboxes\n\
+-p <pri>\t\tSet minimum priority to show\n\
+\n\
+Mailbox options (set with `-o', use upper case to negate):\n\
+0-9\t\t\tSet mailbox priority (0=default)\n\
+b\t\t\tBeep when a message arrives\n\
+e\t\t\tHide from display if empty\n\
+f\t\t\tShow flagged messages if there are no new ones\n\
+h\t\t\tHide from display\n\
+m\t\t\tShow mailbox name of the sender\n\
+p\t\t\tShow personal info (full name) of the sender\n\
+s\t\t\tShow message snippets\n\
+t\t\t\tHighlight the entry\n\
 \n\
-CheckMail 0.1, (c) 2005 Martin Mares <mj@ucw.cz>\n\
+CheckMail " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
 It can be freely distributed and used according to the GNU GPL v2.\n\
 ");
   exit(1);
 }
 
+static void
+parse_options(char *c)
+{
+  struct options *o;
+  char *sep;
+  if (sep = strchr(c, '='))
+    {
+      struct option_node *n = xmalloc(sizeof(*n) + sep-c);
+      memcpy(n->pattern, c, sep-c);
+      n->pattern[sep-c] = 0;
+      clist_add_tail(&options, &n->n);
+      o = &n->o;
+      init_options(o);
+      c = sep+1;
+    }
+  else
+    o = &global_options;
+
+  int x;
+  while (x = *c++)
+    if (x >= '0' && x <= '9')
+      o->priority = x - '0';
+    else
+      {
+       int value = !!islower(x);
+       switch (tolower(x))
+         {
+         case 'b':
+           o->beep = value;
+           break;
+         case 'e':
+           o->hide_if_empty = value;
+           break;
+         case 'f':
+           o->show_flagged = value;
+           break;
+         case 'h':
+           o->hide = value;
+           break;
+         case 'm':
+           o->sender_mbox = value;
+           break;
+         case 'p':
+           o->sender_personal = value;
+           break;
+         case 's':
+           o->snippets = value;
+           break;
+         case 't':
+           o->highlight = value;
+           break;
+         default:
+           fprintf(stderr, "Invalid mailbox option `%c'\n", x);
+           usage();
+         }
+      }
+}
+
 int
 main(int argc, char **argv)
 {
   clist_init(&mboxes);
-  clist_init(&hilites);
+  clist_init(&options);
   clist_init(&patterns);
 
   int c;
-  while ((c = getopt(argc, argv, "c:dh:ilm:")) >= 0)
+  while ((c = getopt(argc, argv, "c:dim:o:p:")) >= 0)
     switch (c)
       {
       case 'c':
@@ -592,27 +892,28 @@ main(int argc, char **argv)
       case 'd':
        debug_mode++;
        break;
-      case 'h':
-       add_mbox(&hilites, optarg, NULL);
-       break;
       case 'i':
-       add_inbox(&patterns);
-       break;
-      case 'l':
-       lock_mboxes = 1;
+       add_inbox();
        break;
       case 'm':
        run_cmd = optarg;
        break;
+      case 'o':
+       parse_options(optarg);
+       break;
+      case 'p':
+       minimum_priority = atol(optarg);
+       break;
       default:
        usage();
       }
   while (optind < argc)
-    add_mbox(&patterns, argv[optind++], NULL);
+    add_pattern(argv[optind++]);
 
+  charset_init();
   term_init();
   scan_and_redraw();
-  next_active(0);
+  next_active(0, 1);
 
   int should_exit = 0;
   while (!should_exit)
@@ -639,7 +940,7 @@ main(int argc, char **argv)
            case KEY_UP:
              move_cursor(cursor_at-1);
              break;
-           case '0':
+           case '^':
            case KEY_HOME:
            case KEY_PPAGE:
              move_cursor(0);
@@ -650,7 +951,10 @@ main(int argc, char **argv)
              move_cursor(cursor_max-1);
              break;
            case '\t':
-             next_active(cursor_at+1);
+             next_active(cursor_at+1, 1);
+             break;
+           case '`':
+             next_active(cursor_at-1, -1);
              break;
            case '\r':
            case '\n':
@@ -668,9 +972,30 @@ main(int argc, char **argv)
                  scan_and_redraw();
                }
              break;
+           case 'l' & 0x1f:
+             clearok(stdscr, TRUE);
+             redraw_all();
+             refresh();
+             break;
            case 'r' & 0x1f:
              force_refresh = 1;
              break;
+           case 'b':
+             allow_bells = 1;
+             print_status("Bells and whistles are now enabled. Toot!");
+             break;
+           case 'B':
+             allow_bells = 0;
+             print_status("Bells and whistles are now disabled. Pssst!");
+             break;
+           default:
+             if (ch >= '0' && ch <= '9')
+               {
+                 minimum_priority = ch - '0';
+                 scan_and_redraw();
+               }
+             else
+               debug("Pressed unknown key %d\n", ch);
            }
          refresh();
        }