]> mj.ucw.cz Git - checkmail.git/blobdiff - cm.c
Implemented incremental search
[checkmail.git] / cm.c
diff --git a/cm.c b/cm.c
index 963fe049541f4b031d53c3fc5e20f70bc3e97600..2d8c6ce1395d6abb7c3b75ad274875a0ce56b109 100644 (file)
--- a/cm.c
+++ b/cm.c
@@ -1,9 +1,11 @@
 /*
  *     Incoming Mail Checker
  *
 /*
  *     Incoming Mail Checker
  *
- *     (c) 2005--2007 Martin Mares <mj@ucw.cz>
+ *     (c) 2005--2010 Martin Mares <mj@ucw.cz>
  */
 
  */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <time.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef CONFIG_WIDE_CURSES
+#include <ncursesw/ncurses.h>
+#else
 #include <curses.h>
 #include <curses.h>
+#endif
 
 #include "util.h"
 #include "clists.h"
 
 #include "util.h"
 #include "clists.h"
@@ -25,6 +34,7 @@
 static int check_interval = 30;
 static int force_refresh;
 static int allow_bells = 1;
 static int check_interval = 30;
 static int force_refresh;
 static int allow_bells = 1;
+static int allow_osd = 1;
 static int minimum_priority;
 static time_t last_scan_time;
 static char *run_cmd = "mutt -f %s";
 static int minimum_priority;
 static time_t last_scan_time;
 static char *run_cmd = "mutt -f %s";
@@ -37,6 +47,11 @@ struct options {
   int beep;
   int snippets;
   int show_flagged;
   int beep;
   int snippets;
   int show_flagged;
+  int sender_personal;
+  int sender_mbox;
+  int hotkey;
+  int led;
+  int osd;
 };
 
 struct option_node {
 };
 
 struct option_node {
@@ -52,7 +67,9 @@ struct pattern_node {
 };
 
 static clist options, patterns;
 };
 
 static clist options, patterns;
-static struct options global_options;
+static struct options global_options = {
+  .sender_personal = 1
+};
 
 struct mbox {
   cnode n;
 
 struct mbox {
   cnode n;
@@ -76,6 +93,14 @@ static clist mboxes;
 static struct mbox **mbox_array;
 static int num_mboxes, mbox_array_size;
 
 static struct mbox **mbox_array;
 static int num_mboxes, mbox_array_size;
 
+struct osd_opt_node {
+  cnode n;
+  char *val;
+  char key[1];
+};
+
+static clist osd_opts;
+
 static void redraw_line(int i);
 static void rethink_display(void);
 
 static void redraw_line(int i);
 static void rethink_display(void);
 
@@ -115,6 +140,11 @@ init_options(struct options *o)
   o->highlight = -1;
   o->snippets = -1;
   o->show_flagged = -1;
   o->highlight = -1;
   o->snippets = -1;
   o->show_flagged = -1;
+  o->sender_personal = -1;
+  o->sender_mbox = -1;
+  o->hotkey = -1;
+  o->led = -1;
+  o->osd = -1;
 }
 
 static void
 }
 
 static void
@@ -133,9 +163,26 @@ setup_options(struct mbox *b)
        MERGE(beep);
        MERGE(snippets);
        MERGE(show_flagged);
        MERGE(beep);
        MERGE(snippets);
        MERGE(show_flagged);
+       MERGE(sender_personal);
+       MERGE(sender_mbox);
+       MERGE(hotkey);
+       MERGE(led);
+       MERGE(osd);
       }
 }
 
       }
 }
 
+static void
+add_osd_opt(char *arg)
+{
+  struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
+  strcpy(n->key, arg);
+  n->val = strchr(n->key, '=');
+  if (!n->val)
+    die("Malformed OSD option");
+  *n->val++ = 0;
+  clist_add_tail(&osd_opts, &n->n);
+}
+
 static char *
 mbox_name(char *path)
 {
 static char *
 mbox_name(char *path)
 {
@@ -214,13 +261,13 @@ prepare_snippet(struct mbox *b, char *sender, char *subject)
 
   char *pos = b->snippet;
   char *term = b->snippet + sizeof(b->snippet) - 1;
 
   char *pos = b->snippet;
   char *term = b->snippet + sizeof(b->snippet) - 1;
-  if (sender[0])
+  if (sender[0] && (b->o.sender_mbox || b->o.sender_personal))
     {
     {
-      add_snippet(&pos, term, sender);
+      add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
       add_snippet(&pos, term, ": ");
     }
   if (subject[0])
       add_snippet(&pos, term, ": ");
     }
   if (subject[0])
-    add_snippet(&pos, term, subject);
+    add_subject_snippet(&pos, term, subject);
   else
     add_snippet(&pos, term, "No subject");
 }
   else
     add_snippet(&pos, term, "No subject");
 }
@@ -295,6 +342,7 @@ scan_mbox(struct mbox *b, struct stat *st)
 {
   char buf[1024], sender[1024], subject[1024];
   int c;
 {
   char buf[1024], sender[1024], subject[1024];
   int c;
+  int compressed = 0;
   const char from[] = "\nFrom ";
 
   if (!st->st_size)
   const char from[] = "\nFrom ";
 
   if (!st->st_size)
@@ -304,7 +352,7 @@ scan_mbox(struct mbox *b, struct stat *st)
       return;
     }
 
       return;
     }
 
-  /* FIXME: Locking! */
+  /* FIXME: Should we do some locking? */
 
   mb_fd = open(b->path, O_RDONLY);
   if (mb_fd < 0)
 
   mb_fd = open(b->path, O_RDONLY);
   if (mb_fd < 0)
@@ -313,10 +361,39 @@ scan_mbox(struct mbox *b, struct stat *st)
       b->total = b->new = b->flagged = -1;
       return;
     }
       b->total = b->new = b->flagged = -1;
       return;
     }
+
+  char signature[2];
+  c = read(mb_fd, signature, 2);
+  lseek(mb_fd, 0, SEEK_SET);
+
+  if (c == 2 && !memcmp(signature, "\037\213", 2)) //gzip
+    {
+      debug("[decompressing] ");
+      int fds[2];
+      if (pipe(fds))
+       die("pipe failed: %m");
+      int pid = fork();
+      if (pid < 0)
+       die("fork failed: %m");
+      if (!pid)
+       {
+         if (dup2(mb_fd, 0) < 0 || dup2(fds[1], 1) < 0)
+           die("dup2 failed: %m");
+         close(fds[0]);
+         close(fds[1]);
+         close(mb_fd);
+         execlp("gzip", "gzip", "-cd", NULL);
+         die("Cannot execute gzip: %m");
+       }
+      close(fds[1]);
+      close(mb_fd);
+      mb_fd = fds[0];
+      compressed = 1;
+    }
   mb_reset(0);
 
   int incremental = 0;
   mb_reset(0);
 
   int incremental = 0;
-  if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh)
+  if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh && !compressed)
     {
       mb_seek(b->last_pos);
       if (mb_check(from, 6))
     {
       mb_seek(b->last_pos);
       if (mb_check(from, 6))
@@ -431,6 +508,12 @@ scan_mbox(struct mbox *b, struct stat *st)
 
  done:
   close(mb_fd);
 
  done:
   close(mb_fd);
+  if (compressed)
+    {
+      int status;
+      if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
+       b->total = b->new = b->flagged = -1;
+    }
 }
 
 static void
 }
 
 static void
@@ -518,17 +601,162 @@ scan(void)
   rethink_display();
 }
 
   rethink_display();
 }
 
+#ifdef CONFIG_X11
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+static Display *x11_dpy;
+static unsigned leds_care, leds_have, leds_want;
+static unsigned osd_care, osd_have, osd_want;
+static Atom osd_pty;
+
+static void
+x11_init(void)
+{
+  leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
+  osd_care = (global_options.osd >= 0);
+  CLIST_FOR_EACH(struct option_node *, o, options)
+    {
+      if (o->o.led > 0)
+       leds_care |= (1 << o->o.led);
+      if (o->o.osd > 0)
+       osd_care = 1;
+    }
+
+  if (!leds_care && !osd_care)
+    {
+      debug("X11: No mailbox wants LEDs or OSD\n");
+      return;
+    }
+  if (!getenv("DISPLAY"))
+    {
+      debug("X11: Do not have X display\n");
+      return;
+    }
+  if (!(x11_dpy = XOpenDisplay(NULL)))
+    die("Cannot open X display, although the DISPLAY variable is set");
+
+  if (osd_care)
+    {
+      osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
+      if (!osd_pty)
+       die("Cannot intern OSD_QUEUE atom");
+
+      // If OSD options contain no message, add one
+      int seen_msg = 0;
+      CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+       if (!n->key[0])
+         seen_msg = 1;
+      if (!seen_msg)
+       add_osd_opt("=You have new mail");
+    }
+
+  leds_have = ~0U;
+  debug("X11: Initialized\n");
+}
+
+static void
+sync_leds(void)
+{
+  if (leds_want == leds_have)
+    return;
+
+  debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
+  for (int i=1; i<10; i++)
+    if (leds_care & (leds_have ^ leds_want) & (1 << i))
+      {
+       XKeyboardControl cc;
+       cc.led = i;
+       cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
+       XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
+      }
+  XFlush(x11_dpy);
+  leds_have = leds_want;
+}
+
+static void
+sync_osd(void)
+{
+  if (!osd_want || !allow_osd)
+    {
+      osd_have = 0;
+      return;
+    }
+  if (osd_have)
+    return;
+  debug("OSD: Displaying\n");
+  osd_have = 1;
+
+  char msg[1024];
+  unsigned pos = 0;
+  CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+    {
+      pos += snprintf(msg+pos, sizeof(msg)-pos-1, "%s:%s\n", n->key, n->val);
+      if (pos > sizeof(msg)-1)
+       {
+         pos = sprintf(msg, "OSD message too long!\n");
+         break;
+       }
+    }
+  msg[pos++] = '\n';
+
+  XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) msg, pos);
+  XFlush(x11_dpy);
+}
+
+static void
+rethink_leds(void)
+{
+  if (!x11_dpy)
+    return;
+
+  leds_want = 0;
+  osd_want = 0;
+  CLIST_FOR_EACH(struct mbox *, b, mboxes)
+    {
+      if (b->o.led > 0 && b->new)
+       leds_want |= (1 << b->o.led);
+      if (b->o.osd > 0 && b->new)
+       osd_want = 1;
+    }
+  sync_leds();
+  sync_osd();
+}
+
+static void
+x11_cleanup(void)
+{
+  if (!x11_dpy)
+    return;
+
+  leds_want = 0;
+  sync_leds();
+}
+
+#else
+
+static void x11_init(void) { }
+static void rethink_leds(void) { }
+static void x11_cleanup(void) { }
+
+#endif
+
 static int cursor_at, cursor_max;
 
 static int cursor_at, cursor_max;
 
+static unsigned is_active, is_pos;     // incremental search
+static char is_buf[64];
+
 enum {
   M_IDLE,
   M_SCAN,
   M_NEW,
   M_FLAG,
   M_BAD,
 enum {
   M_IDLE,
   M_SCAN,
   M_NEW,
   M_FLAG,
   M_BAD,
+  M_INCSEARCH,
   M_MAX
 };
   M_MAX
 };
-static int attrs[2][2][M_MAX];         // active, hilite, status
+static int attrs[3][2][M_MAX];         // active (2=incsearch), hilite, status
 
 static void
 redraw_line(int i)
 
 static void
 redraw_line(int i)
@@ -537,19 +765,34 @@ redraw_line(int i)
   if (i < cursor_max)
     {
       struct mbox *b = mbox_array[i];
   if (i < cursor_max)
     {
       struct mbox *b = mbox_array[i];
-      int cc = (cursor_at == i);
+      int cc = (cursor_at == i ? (is_active ? 2 : 1) : 0);
       int hi = b->o.highlight;
       int hi = b->o.highlight;
+      unsigned namepos = 0;
+      unsigned namelen = strlen(b->name);
 
       attrset(attrs[cc][hi][M_IDLE]);
 
       attrset(attrs[cc][hi][M_IDLE]);
-      if (cc)
+      if (b->o.hotkey)
+       printw("%c ", b->o.hotkey);
+      else if (cc)
        printw("> ");
       else
        printw("  ");
        printw("> ");
       else
        printw("  ");
+      if (cc == 2)
+       {
+         attrset(attrs[cc][hi][M_INCSEARCH]);
+         for (namepos=0; namepos < is_pos && namepos < 20; namepos++)
+           addch(is_buf[namepos]);
+       }
       if (b->new)
        attrset(attrs[cc][hi][M_NEW]);
       if (b->new)
        attrset(attrs[cc][hi][M_NEW]);
-      else if (b->flagged)
+      else if (b->flagged && b->o.show_flagged)
        attrset(attrs[cc][hi][M_FLAG]);
        attrset(attrs[cc][hi][M_FLAG]);
-      printw("%-20s ", b->name);
+      else
+       attrset(attrs[cc][hi][M_IDLE]);
+      while (namepos < namelen)
+       addch(b->name[namepos++]);
+      while (namepos++ < 20)
+       addch(' ');
       if (b->scanning < 0)
        ;
       else if (b->scanning)
       if (b->scanning < 0)
        ;
       else if (b->scanning)
@@ -583,13 +826,14 @@ redraw_line(int i)
                printw("        ");
              snip = 1;
            }
                printw("        ");
              snip = 1;
            }
-         else if (b->flagged)
+         else if (b->flagged && b->o.show_flagged)
            {
              attrset(attrs[cc][hi][M_FLAG]);
              printw("%6d  ", b->flagged);
              attrset(attrs[cc][hi][M_IDLE]);
              printw("        ");
            {
              attrset(attrs[cc][hi][M_FLAG]);
              printw("%6d  ", b->flagged);
              attrset(attrs[cc][hi][M_IDLE]);
              printw("        ");
-             snip = b->o.show_flagged;
+             attrset(attrs[cc][0][M_FLAG]);    /* We avoid the highlight intentionally */
+             snip = 1;
            }
          if (snip && b->o.snippets && b->snippet[0])
            {
            }
          if (snip && b->o.snippets && b->snippet[0])
            {
@@ -597,7 +841,16 @@ redraw_line(int i)
              getyx(stdscr, yy, xx);
              int remains = COLS-1-xx;
              if (remains > 2)
              getyx(stdscr, yy, xx);
              int remains = COLS-1-xx;
              if (remains > 2)
-               printw("%-.*s", remains, b->snippet);
+               {
+#ifdef CONFIG_WIDE_CURSES
+                 size_t len = strlen(b->snippet)+1;
+                 wchar_t snip[len];
+                 mbstowcs(snip, b->snippet, len);
+                 addnwstr(snip, remains);
+#else
+                 printw("%-.*s", remains, b->snippet);
+#endif
+               }
            }
        }
     }
            }
        }
     }
@@ -662,6 +915,7 @@ rethink_display(void)
       redraw_all();
       refresh();
     }
       redraw_all();
       refresh();
     }
+  rethink_leds();
   if (beeeep && allow_bells)
     beep();
 }
   if (beeeep && allow_bells)
     beep();
 }
@@ -678,20 +932,31 @@ term_init(void)
   curs_set(0);
 
   static const int attrs_mono[2][M_MAX] = {
   curs_set(0);
 
   static const int attrs_mono[2][M_MAX] = {
-    [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 },
+       [0] = { [M_IDLE] = 0,
+               [M_SCAN] = A_BOLD,
+               [M_NEW] = A_BOLD,
+               [M_FLAG] = 0,
+               [M_BAD] = A_DIM,
+               [M_INCSEARCH] = A_REVERSE },
+       [1] = { [M_IDLE] = 0,
+               [M_SCAN] = A_BOLD,
+               [M_NEW] = A_REVERSE | A_BOLD,
+               [M_FLAG] = A_REVERSE,
+               [M_BAD] = A_DIM,
+               [M_INCSEARCH] = A_REVERSE },
   };
   for (int i=0; i<2; i++)
     for (int j=0; j<M_MAX; j++)
       {
        attrs[0][i][j] = attrs_mono[i][j];
        attrs[1][i][j] = attrs_mono[i][j] | A_UNDERLINE;
   };
   for (int i=0; i<2; i++)
     for (int j=0; j<M_MAX; j++)
       {
        attrs[0][i][j] = attrs_mono[i][j];
        attrs[1][i][j] = attrs_mono[i][j] | A_UNDERLINE;
+       attrs[2][i][j] = attrs_mono[i][j] | A_UNDERLINE;
       }
 
   if (has_colors())
     {
       start_color();
       }
 
   if (has_colors())
     {
       start_color();
-      if (COLOR_PAIRS >= 5)
+      if (COLOR_PAIRS >= 12)
        {
          init_pair(1, COLOR_YELLOW, COLOR_BLACK);
          init_pair(2, COLOR_RED, COLOR_BLACK);
        {
          init_pair(1, COLOR_YELLOW, COLOR_BLACK);
          init_pair(2, COLOR_RED, COLOR_BLACK);
@@ -700,11 +965,44 @@ term_init(void)
          init_pair(5, COLOR_RED, COLOR_BLUE);
          init_pair(6, COLOR_GREEN, COLOR_BLACK);
          init_pair(7, COLOR_GREEN, 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_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 },
+         init_pair(8, COLOR_WHITE, COLOR_MAGENTA);
+         init_pair(9, COLOR_YELLOW, COLOR_MAGENTA);
+         init_pair(10, COLOR_GREEN, COLOR_MAGENTA);
+         init_pair(11, COLOR_RED, COLOR_MAGENTA);
+         init_pair(12, COLOR_BLACK, COLOR_YELLOW);
+         static const int attrs_color[3][2][M_MAX] = {
+            [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 },
+            [2][0] = { [M_IDLE] = COLOR_PAIR(8),
+                       [M_SCAN] = COLOR_PAIR(9),
+                       [M_NEW] = COLOR_PAIR(9),
+                       [M_FLAG] = COLOR_PAIR(10),
+                       [M_BAD] = COLOR_PAIR(11),
+                       [M_INCSEARCH] = COLOR_PAIR(12) | A_DIM },
+            [2][1] = { [M_IDLE] = COLOR_PAIR(8) | A_BOLD,
+                       [M_SCAN] = COLOR_PAIR(9),
+                       [M_NEW] = COLOR_PAIR(9) | A_BOLD,
+                       [M_FLAG] = COLOR_PAIR(10) | A_BOLD,
+                       [M_BAD] = COLOR_PAIR(11) | A_BOLD,
+                       [M_INCSEARCH] = COLOR_PAIR(12) },
          };
          memcpy(attrs, attrs_color, sizeof(attrs));
        }
          };
          memcpy(attrs, attrs_color, sizeof(attrs));
        }
@@ -772,6 +1070,67 @@ next_active(int since, int step)
     move_cursor(besti);
 }
 
     move_cursor(besti);
 }
 
+static void
+mbox_run(struct mbox *b)
+{
+  char cmd[strlen(run_cmd) + strlen(b->path) + 16];
+  sprintf(cmd, run_cmd, b->path);
+  term_cleanup();
+  system(cmd);
+  term_init();
+  redraw_all();
+  refresh();
+  b->force_refresh = 1;
+  scan_and_redraw();
+}
+
+static void
+enter_incsearch(void)
+{
+  print_status("Incremental search...");
+  is_active = 1;
+  is_pos = 0;
+  redraw_line(cursor_at);
+}
+
+static int
+handle_incsearch(int ch)
+{
+  if ((ch == KEY_BACKSPACE || ch == KEY_DC) && is_pos)
+    --is_pos;
+  else if (ch >= ' ' && ch <= '~')
+    {
+      if (is_pos < sizeof(is_buf) - 1)
+       is_buf[is_pos++] = ch;
+    }
+  else
+    {
+      print_status("");
+      is_active = 0;
+      is_pos = 0;
+      redraw_line(cursor_at);
+      return 0;
+    }
+
+  is_buf[is_pos] = 0;
+  for (int i=0; i<cursor_max; i++)
+    {
+      struct mbox *b = mbox_array[i];
+      if (!strncmp(b->name, is_buf, is_pos))
+       {
+         if (i != cursor_at)
+           {
+             move_cursor(i);
+             return 1;
+           }
+         break;
+       }
+    }
+
+  redraw_line(cursor_at);
+  return 1;
+}
+
 #define STR2(c) #c
 #define STR(c) STR2(c)
 
 #define STR2(c) #c
 #define STR(c) STR2(c)
 
@@ -788,15 +1147,21 @@ Options:\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\
 -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\
+-s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\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\
 \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\
+d\t\t\tSend an on-screen-display message (requires OSDD)\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\
 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\
+l<led>\t\t\tLight a keyboard led (1-9) if running on X 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\
 s\t\t\tShow message snippets\n\
 t\t\t\tHighlight the entry\n\
+!<key>\t\t\tSet hot key\n\
 \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\
 \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\
@@ -826,6 +1191,10 @@ parse_options(char *c)
   while (x = *c++)
     if (x >= '0' && x <= '9')
       o->priority = x - '0';
   while (x = *c++)
     if (x >= '0' && x <= '9')
       o->priority = x - '0';
+    else if (x == '!' && *c)
+      o->hotkey = *c++;
+    else if (x == 'l' && *c >= '1' && *c <= '9')
+      o->led = *c++ - '0';
     else
       {
        int value = !!islower(x);
     else
       {
        int value = !!islower(x);
@@ -834,6 +1203,9 @@ parse_options(char *c)
          case 'b':
            o->beep = value;
            break;
          case 'b':
            o->beep = value;
            break;
+         case 'd':
+           o->osd = value;
+           break;
          case 'e':
            o->hide_if_empty = value;
            break;
          case 'e':
            o->hide_if_empty = value;
            break;
@@ -843,6 +1215,12 @@ parse_options(char *c)
          case 'h':
            o->hide = 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 's':
            o->snippets = value;
            break;
@@ -862,9 +1240,10 @@ main(int argc, char **argv)
   clist_init(&mboxes);
   clist_init(&options);
   clist_init(&patterns);
   clist_init(&mboxes);
   clist_init(&options);
   clist_init(&patterns);
+  clist_init(&osd_opts);
 
   int c;
 
   int c;
-  while ((c = getopt(argc, argv, "c:dim:o:p:")) >= 0)
+  while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
     switch (c)
       {
       case 'c':
     switch (c)
       {
       case 'c':
@@ -887,6 +1266,9 @@ main(int argc, char **argv)
       case 'p':
        minimum_priority = atol(optarg);
        break;
       case 'p':
        minimum_priority = atol(optarg);
        break;
+      case 's':
+       add_osd_opt(optarg);
+       break;
       default:
        usage();
       }
       default:
        usage();
       }
@@ -895,10 +1277,12 @@ main(int argc, char **argv)
 
   charset_init();
   term_init();
 
   charset_init();
   term_init();
+  x11_init();
   scan_and_redraw();
   next_active(0, 1);
 
   int should_exit = 0;
   scan_and_redraw();
   next_active(0, 1);
 
   int should_exit = 0;
+restart:
   while (!should_exit)
     {
       time_t now = time(NULL);
   while (!should_exit)
     {
       time_t now = time(NULL);
@@ -910,6 +1294,21 @@ main(int argc, char **argv)
          remains *= 10;
          halfdelay((remains > 255) ? 255 : remains);
          int ch = getch();
          remains *= 10;
          halfdelay((remains > 255) ? 255 : remains);
          int ch = getch();
+         if (ch < 0)
+           continue;
+         if (is_active && handle_incsearch(ch))
+           {
+             refresh();
+             continue;
+           }
+         for (int i=0; i<num_mboxes; i++)
+           if (ch == mbox_array[i]->o.hotkey)
+             {
+               if (i < cursor_max)
+                 cursor_at = i;
+               mbox_run(mbox_array[i]);
+               goto restart;
+             }
          switch (ch)
            {
            case 'q':
          switch (ch)
            {
            case 'q':
@@ -942,18 +1341,7 @@ main(int argc, char **argv)
            case '\r':
            case '\n':
              if (cursor_at < cursor_max)
            case '\r':
            case '\n':
              if (cursor_at < cursor_max)
-               {
-                 struct mbox *b = mbox_array[cursor_at];
-                 char cmd[strlen(run_cmd) + strlen(b->path) + 16];
-                 sprintf(cmd, run_cmd, b->path);
-                 term_cleanup();
-                 system(cmd);
-                 term_init();
-                 redraw_all();
-                 refresh();
-                 b->force_refresh = 1;
-                 scan_and_redraw();
-               }
+               mbox_run(mbox_array[cursor_at]);
              break;
            case 'l' & 0x1f:
              clearok(stdscr, TRUE);
              break;
            case 'l' & 0x1f:
              clearok(stdscr, TRUE);
@@ -971,6 +1359,17 @@ main(int argc, char **argv)
              allow_bells = 0;
              print_status("Bells and whistles are now disabled. Pssst!");
              break;
              allow_bells = 0;
              print_status("Bells and whistles are now disabled. Pssst!");
              break;
+           case 'd':
+             allow_osd = 1;
+             print_status("On-screen display is now enabled.");
+             break;
+           case 'D':
+             allow_osd = 0;
+             print_status("On-screen display is now disabled. Watch your step.");
+             break;
+           case '/':
+             enter_incsearch();
+             break;
            default:
              if (ch >= '0' && ch <= '9')
                {
            default:
              if (ch >= '0' && ch <= '9')
                {
@@ -984,6 +1383,7 @@ main(int argc, char **argv)
        }
     }
 
        }
     }
 
+  x11_cleanup();
   term_cleanup();
   return 0;
 }
   term_cleanup();
   return 0;
 }