]> mj.ucw.cz Git - checkmail.git/blobdiff - cm.c
Fixed a bug in parsing of headers
[checkmail.git] / cm.c
diff --git a/cm.c b/cm.c
index 12ff25e9ecc1e9125614badb7e66f2755493f465..642c5e54dec089b34d7ff2f26a0513b1e9e209b3 100644 (file)
--- a/cm.c
+++ b/cm.c
@@ -38,6 +38,7 @@ static int allow_osd = 1;
 static int minimum_priority;
 static time_t last_scan_time;
 static char *run_cmd = "mutt -f %s";
+static int simple_tab;
 
 struct options {
   int priority;
@@ -52,6 +53,7 @@ struct options {
   int hotkey;
   int led;
   int osd;
+  int unread_is_new;
 };
 
 struct option_node {
@@ -80,13 +82,15 @@ struct mbox {
   int scanning;
   int seen;
   time_t last_time;
+  time_t display_valid_until;
   int last_size, last_pos;
   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];
+  char sender_snippet[128];
+  char subject_snippet[128];
 };
 
 static clist mboxes;
@@ -102,7 +106,7 @@ struct osd_opt_node {
 static clist osd_opts;
 
 static void redraw_line(int i);
-static void rethink_display(void);
+static void rethink_display(int notify);
 
 static void
 add_pattern(char *patt)
@@ -145,6 +149,7 @@ init_options(struct options *o)
   o->hotkey = -1;
   o->led = -1;
   o->osd = -1;
+  o->unread_is_new = -1;
 }
 
 static void
@@ -168,6 +173,7 @@ setup_options(struct mbox *b)
        MERGE(hotkey);
        MERGE(led);
        MERGE(osd);
+       MERGE(unread_is_new);
       }
 }
 
@@ -252,24 +258,39 @@ mbox_visible_p(struct mbox *b)
 }
 
 static void
-prepare_snippet(struct mbox *b, char *sender, char *subject)
+prepare_snippets(struct mbox *b, char *sender, char *subject)
 {
+  char *pos, *term;
+
   while (*sender == ' ' || *sender == '\t')
     sender++;
   while (*subject == ' ' || *subject == '\t')
     subject++;
 
-  char *pos = b->snippet;
-  char *term = b->snippet + sizeof(b->snippet) - 1;
+  pos = b->sender_snippet;
+  term = pos + sizeof(b->sender_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, ": ");
-    }
+    add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
+  else
+    *pos = 0;
+
+  pos = b->subject_snippet;
+  term = pos + sizeof(b->subject_snippet) - 1;
   if (subject[0])
     add_subject_snippet(&pos, term, subject);
   else
-    add_snippet(&pos, term, "No subject");
+    add_snippet_raw(&pos, term, "No subject");
+}
+
+static void
+build_snippet(char *buf, char *term, struct mbox *b)
+{
+  if (b->sender_snippet[0])
+    {
+      add_snippet(&buf, term, b->sender_snippet);
+      add_snippet_raw(&buf, term, ": ");
+    }
+  add_snippet(&buf, term, b->subject_snippet);
 }
 
 static int mb_fd, mb_pos;
@@ -337,6 +358,27 @@ mb_check(const char *p, int len)
   return 1;
 }
 
+static void
+mb_skip(int len)
+{
+  while (len)
+    {
+      int avail = mb_end - mb_cc;
+      if (!avail)
+       {
+         if (mb_ll_get() < 0)
+           return;
+         len--;
+       }
+      else
+       {
+         int next = (avail < len) ? avail : len;
+         len -= next;
+         mb_cc += next;
+       }
+    }
+}
+
 static void
 scan_mbox(struct mbox *b, struct stat *st)
 {
@@ -437,6 +479,7 @@ scan_mbox(struct mbox *b, struct stat *st)
       while ((c = mb_get()) >= 0 && c != '\n')
        ;
 
+      int content_length = -1;
       int new = 1;
       int flagged = 0;
       sender[0] = 0;
@@ -454,6 +497,8 @@ scan_mbox(struct mbox *b, struct stat *st)
                }
              if (c == '\n')
                {
+                 if (!i)
+                   break;
                  int fold = -1;
                  do
                    {
@@ -475,13 +520,18 @@ scan_mbox(struct mbox *b, struct stat *st)
          if (!buf[0])
            break;
          if (!strncasecmp(buf, "Status:", 7))
-           new = 0;
+           {
+             if (!b->o.unread_is_new || strchr(buf + 7, 'R'))
+               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);
+         else if (!strncasecmp(buf, "Content-Length:", 15))
+           content_length = atoi(buf + 15);
        }
 
       b->total++;
@@ -489,12 +539,17 @@ scan_mbox(struct mbox *b, struct stat *st)
        b->new++;
       if (flagged)
        b->flagged++;
+      if (debug_mode > 1)
+       debug("new=%d flagged=%d len=%d sender=<%s> subject=<%s>\n", new, flagged, content_length, sender, subject);
       if (new || (flagged && !b->snippet_is_new))
        {
          b->snippet_is_new = new;
-         prepare_snippet(b, sender, subject);
+         prepare_snippets(b, sender, subject);
        }
 
+      if (content_length >= 0)
+       mb_skip(content_length);
+
       int ct = 1;
       while (from[ct])
        {
@@ -517,9 +572,9 @@ scan_mbox(struct mbox *b, struct stat *st)
 }
 
 static void
-scan(void)
+scan(int notify)
 {
-  debug("Searching for mailboxes...\n");
+  debug("Searching for mailboxes (notify=%d)...\n", notify);
   last_scan_time = time(NULL);
   CLIST_FOR_EACH(struct pattern_node *, p, patterns)
     {
@@ -556,7 +611,7 @@ scan(void)
        }
     }
 
-  rethink_display();
+  rethink_display(0);
 
   debug("Scanning mailboxes...\n");
   CLIST_FOR_EACH(struct mbox *, b, mboxes)
@@ -590,6 +645,11 @@ scan(void)
          redraw_line(b->index);
          refresh();
        }
+      else if (b->display_valid_until <= last_scan_time)
+       {
+         debug("not changed, but needs redraw\n");
+         redraw_line(b->index);
+       }
       else
        debug("not changed\n");
       b->force_refresh = 0;
@@ -598,7 +658,7 @@ scan(void)
 
   debug("Scan finished\n");
   last_scan_time = time(NULL);
-  rethink_display();
+  rethink_display(notify);
 }
 
 #ifdef CONFIG_X11
@@ -608,8 +668,12 @@ scan(void)
 
 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 unsigned osd_care;
+#define OSD_MSG_SIZE 1024
+static char osd_last_msg[OSD_MSG_SIZE];
+static time_t osd_last_time;
 
 static void
 x11_init(void)
@@ -649,7 +713,12 @@ x11_init(void)
        if (!n->key[0])
          seen_msg = 1;
       if (!seen_msg)
-       add_osd_opt("=You have new mail");
+       {
+         add_osd_opt("=%40f");
+         add_osd_opt("=%40s");
+         add_osd_opt("=");
+         add_osd_opt("=(and %m more)");
+       }
     }
 
   leds_have = ~0U;
@@ -676,52 +745,162 @@ sync_leds(void)
 }
 
 static void
-sync_osd(void)
+rethink_leds(void)
 {
-  if (!osd_want || !allow_osd)
+  if (!leds_care || !x11_dpy)
+    return;
+
+  leds_want = 0;
+  CLIST_FOR_EACH(struct mbox *, b, mboxes)
+    if (b->o.led > 0 && b->new)
+      leds_want |= (1 << b->o.led);
+  sync_leds();
+}
+
+struct osd_params {
+  struct mbox *mbox;
+  int total_new;
+};
+
+static int
+format_osd_string(char *dest, char *src, struct osd_params *par)
+{
+  char *stop = dest + OSD_MSG_SIZE - 1;
+  char numbuf[16];
+
+  while (*src && dest < stop)
     {
-      osd_have = 0;
+      if (*src == '%')
+       {
+         src++;
+         int size = 0;
+         while (*src >= '0' && *src <= '9')
+           size = 10*size + *src++ - '0';
+         if (!size || size > stop-dest)
+           size = dest-stop;
+
+         int spec = *src++;
+         if (!spec)
+           break;
+
+         char *arg = numbuf;
+         switch (spec)
+           {
+           case 'f':
+             arg = par->mbox->sender_snippet;
+             break;
+           case 's':
+             arg = par->mbox->subject_snippet;
+             break;
+           case 'n':
+             snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
+             break;
+           case 'm':
+             if (par->total_new < 2)
+               return 0;
+             snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
+             break;
+           case '%':
+             arg = "%";
+             break;
+           default:
+             arg = "???";
+             break;
+           }
+
+         while (*arg && size)
+           {
+             *dest++ = *arg++;
+             size--;
+           }
+       }
+      else
+       *dest++ = *src++;
+    }
+  *dest = 0;
+  return 1;
+}
+
+static void
+format_osd(char *msg, struct osd_params *par)
+{
+  if (!par->mbox)
+    {
+      msg[0] = 0;
       return;
     }
-  if (osd_have)
-    return;
-  debug("OSD: Displaying\n");
-  osd_have = 1;
 
-  char msg[1024];
   unsigned pos = 0;
+  unsigned have_text = 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)
+      char buf[OSD_MSG_SIZE];
+      if (!format_osd_string(buf, n->val, par))
+       continue;
+      if (!n->key[0] && buf[0])
+       have_text = 1;
+      pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf);
+      if (pos > OSD_MSG_SIZE-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);
+  if (have_text)
+    msg[pos++] = '\n';
+  else
+    pos = 0;
+  msg[pos] = 0;
 }
 
 static void
-rethink_leds(void)
+debug_osd_msg(char *msg)
 {
-  if (!x11_dpy)
+  if (!debug_mode)
     return;
+  fprintf(stderr, "OSD: <");
+  while (*msg)
+    {
+      fputc((*msg != '\n' ? *msg : '|'), stderr);
+      msg++;
+    }
+  fprintf(stderr, ">\n");
+}
 
-  leds_want = 0;
-  osd_want = 0;
+static void
+rethink_osd(int notify)
+{
+  if (!osd_care || !x11_dpy || !allow_osd)
+    {
+      osd_last_msg[0] = 0;
+      return;
+    }
+
+  struct osd_params p = { .mbox = NULL, .total_new = 0 };
   CLIST_FOR_EACH(struct mbox *, b, mboxes)
+    if (b->o.osd > 0)
+      {
+       p.total_new += b->new;
+       if (b->new && b->last_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
+         p.mbox = b;
+      }
+
+  char new_msg[OSD_MSG_SIZE];
+  format_osd(new_msg, &p);
+  debug_osd_msg(new_msg);
+  if (strcmp(new_msg, osd_last_msg))
     {
-      if (b->o.led > 0 && b->new)
-       leds_want |= (1 << b->o.led);
-      if (b->o.osd > 0 && b->new)
-       osd_want = 1;
+      strcpy(osd_last_msg, new_msg);
+      if (notify && new_msg[0])
+       {
+         debug("OSD: Sending to daemon\n");
+         XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg));
+         XFlush(x11_dpy);
+       }
+      else
+       debug("OSD: No changes\n");
+      osd_last_time = time(NULL);
     }
-  sync_leds();
-  sync_osd();
 }
 
 static void
@@ -738,21 +917,26 @@ x11_cleanup(void)
 
 static void x11_init(void) { }
 static void rethink_leds(void) { }
+static void rethink_osd(int notify UNUSED) { }
 static void x11_cleanup(void) { }
 
 #endif
 
 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,
+  M_INCSEARCH,
   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)
@@ -761,8 +945,11 @@ redraw_line(int 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;
+      unsigned namepos = 0;
+      unsigned namelen = strlen(b->name);
+      int valid = 3600;
 
       attrset(attrs[cc][hi][M_IDLE]);
       if (b->o.hotkey)
@@ -771,11 +958,22 @@ redraw_line(int i)
        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]);
       else if (b->flagged && b->o.show_flagged)
        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)
@@ -802,9 +1000,12 @@ redraw_line(int i)
              if (age < 0)
                age = 0;
              if (age < 3600)
-               printw("%2d min  ", age/60);
+               {
+                 printw("%2d min  ", age/60);
+                 valid = 60;
+               }
              else if (age < 86400)
-               printw("%2d hrs  ", age/3600);
+               printw("%2d hr%c  ", age/3600, (age >= 7200 ? 's' : ' '));
              else
                printw("        ");
              snip = 1;
@@ -818,24 +1019,30 @@ redraw_line(int i)
              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)
            {
              int xx, yy;
              getyx(stdscr, yy, xx);
              int remains = COLS-1-xx;
-             if (remains > 2)
+             (void) yy;
+
+             char snip[256];
+             build_snippet(snip, snip + sizeof(snip) - 1, b);
+
+             if (snip[0] && remains > 2)
                {
 #ifdef CONFIG_WIDE_CURSES
-                 size_t len = strlen(b->snippet)+1;
-                 wchar_t snip[len];
-                 mbstowcs(snip, b->snippet, len);
-                 addnwstr(snip, remains);
+                 size_t len = strlen(snip)+1;
+                 wchar_t snip2[len];
+                 mbstowcs(snip2, snip, len);
+                 addnwstr(snip2, remains);
 #else
-                 printw("%-.*s", remains, b->snippet);
+                 printw("%-.*s", remains, snip);
 #endif
                }
            }
        }
+      b->display_valid_until = last_scan_time + valid;
     }
   attrset(attrs[0][0][M_IDLE]);
   clrtoeol();
@@ -865,7 +1072,7 @@ redraw_all(void)
 }
 
 static void
-rethink_display(void)
+rethink_display(int notify)
 {
   int i = 0;
   int changed = 0;
@@ -899,7 +1106,8 @@ rethink_display(void)
       refresh();
     }
   rethink_leds();
-  if (beeeep && allow_bells)
+  rethink_osd(notify);
+  if (beeeep && allow_bells && notify)
     beep();
 }
 
@@ -915,20 +1123,31 @@ 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_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;
+       attrs[2][i][j] = attrs_mono[i][j] | A_UNDERLINE;
       }
 
   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);
@@ -937,11 +1156,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);
-         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));
        }
@@ -965,10 +1217,10 @@ print_status(char *status)
 }
 
 static void
-scan_and_redraw(void)
+scan_and_redraw(int notify)
 {
   print_status("Busy...");
-  scan();
+  scan(notify);
   print_status(NULL);
 }
 
@@ -997,10 +1249,21 @@ next_active(int since, int step)
   do
     {
       struct mbox *b = mbox_array[i];
-      if (b->new && b->o.priority > bestp)
+      if (simple_tab)
+       {
+         if (b->new)
+           {
+             besti = i;
+             break;
+           }
+       }
+      else
        {
-         besti = i;
-         bestp = b->o.priority;
+         if (b->new && b->o.priority > bestp)
+           {
+             besti = i;
+             bestp = b->o.priority;
+           }
        }
       i = (i+step) % cursor_max;
     }
@@ -1020,7 +1283,54 @@ mbox_run(struct mbox *b)
   redraw_all();
   refresh();
   b->force_refresh = 1;
-  scan_and_redraw();
+  scan_and_redraw(0);
+}
+
+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(NULL);
+      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
@@ -1040,6 +1350,7 @@ 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\
+-t\t\t\tLet TAB select the next mailbox with new mail, no matter what priority it has\n\
 \n\
 Mailbox options (set with `-o', use upper case to negate):\n\
 0-9\t\t\tSet mailbox priority (0=default)\n\
@@ -1050,6 +1361,7 @@ 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\
+o\t\t\tCount old, but unread messages as new\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\
@@ -1110,6 +1422,9 @@ parse_options(char *c)
          case 'm':
            o->sender_mbox = value;
            break;
+         case 'o':
+           o->unread_is_new = value;
+           break;
          case 'p':
            o->sender_personal = value;
            break;
@@ -1135,7 +1450,7 @@ main(int argc, char **argv)
   clist_init(&osd_opts);
 
   int c;
-  while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
+  while ((c = getopt(argc, argv, "c:dim:o:p:s:t")) >= 0)
     switch (c)
       {
       case 'c':
@@ -1161,6 +1476,9 @@ main(int argc, char **argv)
       case 's':
        add_osd_opt(optarg);
        break;
+      case 't':
+       simple_tab = 1;
+       break;
       default:
        usage();
       }
@@ -1170,7 +1488,7 @@ main(int argc, char **argv)
   charset_init();
   term_init();
   x11_init();
-  scan_and_redraw();
+  scan_and_redraw(0);
   next_active(0, 1);
 
   int should_exit = 0;
@@ -1179,13 +1497,22 @@ restart:
     {
       time_t now = time(NULL);
       int remains = last_scan_time + check_interval - now;
-      if (remains <= 0 || force_refresh)
-       scan_and_redraw();
+      if (force_refresh)
+       scan_and_redraw(0);
+      if (remains <= 0)
+       scan_and_redraw(1);
       else
        {
          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)
              {
@@ -1252,11 +1579,14 @@ restart:
              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')
                {
                  minimum_priority = ch - '0';
-                 scan_and_redraw();
+                 scan_and_redraw(0);
                }
              else
                debug("Pressed unknown key %d\n", ch);