2 * Incoming Mail Checker
4 * (c) 2005--2010 Martin Mares <mj@ucw.cz>
21 #include <sys/types.h>
24 #ifdef CONFIG_WIDE_CURSES
25 #include <ncursesw/ncurses.h>
34 static int check_interval = 30;
35 static int force_refresh;
36 static int allow_bells = 1;
37 static int allow_osd = 1;
38 static int minimum_priority;
39 static time_t last_scan_time;
40 static char *run_cmd = "mutt -f %s";
70 static clist options, patterns;
71 static struct options global_options = {
84 time_t display_valid_until;
85 int last_size, last_pos;
86 int total, new, flagged;
87 int last_total, last_new, last_flagged;
91 char sender_snippet[128];
92 char subject_snippet[128];
96 static struct mbox **mbox_array;
97 static int num_mboxes, mbox_array_size;
105 static clist osd_opts;
107 static void redraw_line(int i);
108 static void rethink_display(int notify);
111 add_pattern(char *patt)
113 struct pattern_node *n = xmalloc(sizeof(*n) + strlen(patt));
114 strcpy(n->pattern, patt);
115 if (patt = strchr(n->pattern, '='))
122 clist_add_tail(&patterns, &n->n);
128 struct passwd *p = getpwuid(getuid());
130 die("You don't exist, go away!");
131 char buf[sizeof("/var/mail/") + strlen(p->pw_name) + 7];
132 sprintf(buf, "/var/mail/%s=INBOX", p->pw_name);
137 init_options(struct options *o)
141 o->hide_if_empty = -1;
145 o->show_flagged = -1;
146 o->sender_personal = -1;
151 o->unread_is_new = -1;
155 setup_options(struct mbox *b)
157 b->o = global_options;
158 CLIST_FOR_EACH(struct option_node *, n, options)
159 if (!fnmatch(n->pattern, b->name, 0))
161 debug("\tApplied options %s\n", n->pattern);
162 #define MERGE(f) if (n->o.f >= 0) b->o.f = n->o.f
165 MERGE(hide_if_empty);
170 MERGE(sender_personal);
175 MERGE(unread_is_new);
180 add_osd_opt(char *arg)
182 struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
184 n->val = strchr(n->key, '=');
186 die("Malformed OSD option");
188 clist_add_tail(&osd_opts, &n->n);
192 mbox_name(char *path)
194 char *c = strrchr(path, '/');
195 return c ? (c+1) : path;
199 add_mbox(clist *l, char *path, char *name)
201 struct mbox *b = xmalloc(sizeof(*b));
202 bzero(b, sizeof(*b));
203 b->path = xstrdup(path);
204 b->name = xstrdup(name);
208 cnode *prev = l->head.prev;
209 while (prev != &l->head && strcmp(((struct mbox *)prev)->name, name) > 0)
211 clist_insert_after(&b->n, prev);
214 clist_add_tail(l, &b->n);
220 del_mbox(struct mbox *b)
229 find_mbox(clist *l, char *path)
231 CLIST_FOR_EACH(struct mbox *, b, *l)
232 if (!strcmp(b->path, path))
238 mbox_active_p(struct mbox *b)
240 if (b->o.priority < minimum_priority)
248 mbox_visible_p(struct mbox *b)
250 if (!mbox_active_p(b))
254 if (b->o.hide_if_empty && !b->total)
260 prepare_snippets(struct mbox *b, char *sender, char *subject)
264 while (*sender == ' ' || *sender == '\t')
266 while (*subject == ' ' || *subject == '\t')
269 pos = b->sender_snippet;
270 term = pos + sizeof(b->sender_snippet) - 1;
271 if (sender[0] && (b->o.sender_mbox || b->o.sender_personal))
272 add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
276 pos = b->subject_snippet;
277 term = pos + sizeof(b->subject_snippet) - 1;
279 add_subject_snippet(&pos, term, subject);
281 add_snippet_raw(&pos, term, "No subject");
285 build_snippet(char *buf, char *term, struct mbox *b)
287 if (b->sender_snippet[0])
289 add_snippet(&buf, term, b->sender_snippet);
290 add_snippet_raw(&buf, term, ": ");
292 add_snippet(&buf, term, b->subject_snippet);
295 static int mb_fd, mb_pos;
296 static unsigned char mb_buf[4096], *mb_cc, *mb_end;
301 mb_cc = mb_end = mb_buf;
308 lseek(mb_fd, pos, SEEK_SET);
315 return mb_pos - (mb_end - mb_cc);
321 int len = read(mb_fd, mb_buf, sizeof(mb_buf));
330 mb_end = mb_buf + len;
339 return (mb_cc < mb_end) ? *mb_cc++ : mb_ll_get();
350 mb_check(const char *p, int len)
354 if (mb_get() != *p++)
365 int avail = mb_end - mb_cc;
374 int next = (avail < len) ? avail : len;
382 scan_mbox(struct mbox *b, struct stat *st)
384 char buf[1024], sender[1024], subject[1024];
387 const char from[] = "\nFrom ";
391 b->total = b->new = b->flagged = 0;
396 /* FIXME: Should we do some locking? */
398 mb_fd = open(b->path, O_RDONLY);
401 debug("[open failed: %m] ");
402 b->total = b->new = b->flagged = -1;
407 c = read(mb_fd, signature, 2);
408 lseek(mb_fd, 0, SEEK_SET);
410 if (c == 2 && !memcmp(signature, "\037\213", 2)) //gzip
412 debug("[decompressing] ");
415 die("pipe failed: %m");
418 die("fork failed: %m");
421 if (dup2(mb_fd, 0) < 0 || dup2(fds[1], 1) < 0)
422 die("dup2 failed: %m");
426 execlp("gzip", "gzip", "-cd", NULL);
427 die("Cannot execute gzip: %m");
437 if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh && !compressed)
439 mb_seek(b->last_pos);
440 if (mb_check(from, 6))
442 debug("[incremental] ");
447 debug("[incremental failed] ");
453 if (!mb_check(from+1, 5))
455 debug("[inconsistent] ");
456 b->total = b->new = b->flagged = -1;
459 b->total = b->new = b->flagged = 0;
460 b->last_total = b->last_new = b->last_flagged = 0;
461 b->snippet_is_new = 0;
465 b->total = b->last_total;
466 b->new = b->last_new;
467 b->flagged = b->last_flagged;
472 b->last_pos = mb_tell() - 5;
474 b->last_pos--; // last_pos should be the previous \n character
475 b->last_total = b->total;
476 b->last_new = b->new;
477 b->last_flagged = b->flagged;
478 while ((c = mb_get()) >= 0 && c != '\n')
481 int content_length = -1;
494 debug("[truncated] ");
505 while (c == ' ' || c == '\t');
513 if (i < sizeof(buf) - 1)
519 if (!strncasecmp(buf, "Status:", 7))
521 if (!b->o.unread_is_new || strchr(buf + 7, 'R'))
524 else if (!strncasecmp(buf, "X-Status:", 9) && strchr(buf+9, 'F'))
526 else if (!strncasecmp(buf, "From:", 5))
527 strcpy(sender, buf+5);
528 else if (!strncasecmp(buf, "Subject:", 8))
529 strcpy(subject, buf+8);
530 else if (!strncasecmp(buf, "Content-Length:", 15))
531 content_length = atoi(buf + 15);
540 debug("new=%d flagged=%d len=%d sender=<%s> subject=<%s>\n", new, flagged, content_length, sender, subject);
541 if (new || (flagged && !b->snippet_is_new))
543 b->snippet_is_new = new;
544 prepare_snippets(b, sender, subject);
547 if (content_length >= 0)
548 mb_skip(content_length);
566 if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
567 b->total = b->new = b->flagged = -1;
574 debug("Searching for mailboxes (notify=%d)...\n", notify);
575 last_scan_time = time(NULL);
576 CLIST_FOR_EACH(struct pattern_node *, p, patterns)
578 debug("Trying pattern %s (name %s)\n", p->pattern, p->name);
580 int err = glob(p->pattern, GLOB_ERR | GLOB_NOSORT | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &g);
581 if (err && err != GLOB_NOMATCH)
582 die("Failed to glob %s: %m", p->pattern);
583 for (uns i=0; i<g.gl_pathc; i++)
585 char *name = g.gl_pathv[i];
586 struct mbox *b = find_mbox(&mboxes, name);
589 b = add_mbox(&mboxes, name, (p->name ? p->name : mbox_name(name)));
590 debug("Discovered mailbox %s (%s)\n", b->name, b->path);
600 CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
606 debug("Lost mailbox %s\n", b->name);
613 debug("Scanning mailboxes...\n");
614 CLIST_FOR_EACH(struct mbox *, b, mboxes)
617 debug("%s: ", b->name);
618 if (!mbox_active_p(b))
624 b->force_refresh = 1;
625 if (stat(b->path, &st) < 0)
627 b->total = b->new = b->flagged = -1;
630 else if (!b->last_time || st.st_mtime != b->last_time || st.st_size != b->last_size || b->force_refresh)
633 redraw_line(b->index);
637 b->last_time = st.st_mtime;
638 b->last_size = st.st_size;
639 debug("%d %d %d (stopped at %d of %d)\n", b->total, b->new, b->flagged, b->last_pos, b->last_size);
642 redraw_line(b->index);
645 else if (b->display_valid_until <= last_scan_time)
647 debug("not changed, but needs redraw\n");
648 redraw_line(b->index);
651 debug("not changed\n");
652 b->force_refresh = 0;
656 debug("Scan finished\n");
657 last_scan_time = time(NULL);
658 rethink_display(notify);
663 #include <X11/Xlib.h>
664 #include <X11/Xatom.h>
666 static Display *x11_dpy;
667 static unsigned leds_care, leds_have, leds_want;
670 static unsigned osd_care;
671 #define OSD_MSG_SIZE 1024
672 static char osd_last_msg[OSD_MSG_SIZE];
673 static time_t osd_last_time;
678 leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
679 osd_care = (global_options.osd >= 0);
680 CLIST_FOR_EACH(struct option_node *, o, options)
683 leds_care |= (1 << o->o.led);
688 if (!leds_care && !osd_care)
690 debug("X11: No mailbox wants LEDs or OSD\n");
693 if (!getenv("DISPLAY"))
695 debug("X11: Do not have X display\n");
698 if (!(x11_dpy = XOpenDisplay(NULL)))
699 die("Cannot open X display, although the DISPLAY variable is set");
703 osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
705 die("Cannot intern OSD_QUEUE atom");
707 // If OSD options contain no message, add one
709 CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
714 add_osd_opt("=%40f");
715 add_osd_opt("=%40s");
717 add_osd_opt("=(and %m more)");
722 debug("X11: Initialized\n");
728 if (leds_want == leds_have)
731 debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
732 for (int i=1; i<10; i++)
733 if (leds_care & (leds_have ^ leds_want) & (1 << i))
737 cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
738 XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
741 leds_have = leds_want;
747 if (!leds_care || !x11_dpy)
751 CLIST_FOR_EACH(struct mbox *, b, mboxes)
752 if (b->o.led > 0 && b->new)
753 leds_want |= (1 << b->o.led);
763 format_osd_string(char *dest, char *src, struct osd_params *par)
765 char *stop = dest + OSD_MSG_SIZE - 1;
768 while (*src && dest < stop)
774 while (*src >= '0' && *src <= '9')
775 size = 10*size + *src++ - '0';
776 if (!size || size > stop-dest)
787 arg = par->mbox->sender_snippet;
790 arg = par->mbox->subject_snippet;
793 snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
796 if (par->total_new < 2)
798 snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
822 format_osd(char *msg, struct osd_params *par)
831 unsigned have_text = 0;
832 CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
834 char buf[OSD_MSG_SIZE];
835 if (!format_osd_string(buf, n->val, par))
837 if (!n->key[0] && buf[0])
839 pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf);
840 if (pos > OSD_MSG_SIZE-1)
842 pos = sprintf(msg, "OSD message too long!\n");
854 debug_osd_msg(char *msg)
858 fprintf(stderr, "OSD: <");
861 fputc((*msg != '\n' ? *msg : '|'), stderr);
864 fprintf(stderr, ">\n");
868 rethink_osd(int notify)
870 if (!osd_care || !x11_dpy || !allow_osd)
876 struct osd_params p = { .mbox = NULL, .total_new = 0 };
877 CLIST_FOR_EACH(struct mbox *, b, mboxes)
880 p.total_new += b->new;
881 if (b->new && b->last_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
885 char new_msg[OSD_MSG_SIZE];
886 format_osd(new_msg, &p);
887 debug_osd_msg(new_msg);
888 if (strcmp(new_msg, osd_last_msg))
890 strcpy(osd_last_msg, new_msg);
891 if (notify && new_msg[0])
893 debug("OSD: Sending to daemon\n");
894 XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg));
898 debug("OSD: No changes\n");
899 osd_last_time = time(NULL);
915 static void x11_init(void) { }
916 static void rethink_leds(void) { }
917 static void rethink_osd(int notify UNUSED) { }
918 static void x11_cleanup(void) { }
922 static int cursor_at, cursor_max;
924 static unsigned is_active, is_pos; // incremental search
925 static char is_buf[64];
936 static int attrs[3][2][M_MAX]; // active (2=incsearch), hilite, status
944 struct mbox *b = mbox_array[i];
945 int cc = (cursor_at == i ? (is_active ? 2 : 1) : 0);
946 int hi = b->o.highlight;
947 unsigned namepos = 0;
948 unsigned namelen = strlen(b->name);
951 attrset(attrs[cc][hi][M_IDLE]);
953 printw("%c ", b->o.hotkey);
960 attrset(attrs[cc][hi][M_INCSEARCH]);
961 for (namepos=0; namepos < is_pos && namepos < 20; namepos++)
962 addch(is_buf[namepos]);
965 attrset(attrs[cc][hi][M_NEW]);
966 else if (b->flagged && b->o.show_flagged)
967 attrset(attrs[cc][hi][M_FLAG]);
969 attrset(attrs[cc][hi][M_IDLE]);
970 while (namepos < namelen)
971 addch(b->name[namepos++]);
972 while (namepos++ < 20)
976 else if (b->scanning)
978 attrset(attrs[cc][hi][M_SCAN]);
979 printw("[SCANNING]");
981 else if (b->total < 0)
983 attrset(attrs[cc][hi][M_BAD]);
988 attrset(attrs[cc][hi][M_IDLE]);
989 printw("%6d ", b->total);
993 attrset(attrs[cc][hi][M_NEW]);
994 printw("%6d ", b->new);
995 attrset(attrs[cc][hi][M_IDLE]);
996 int age = (last_scan_time - b->last_time);
1001 printw("%2d min ", age/60);
1004 else if (age < 86400)
1005 printw("%2d hr%c ", age/3600, (age >= 7200 ? 's' : ' '));
1010 else if (b->flagged && b->o.show_flagged)
1012 attrset(attrs[cc][hi][M_FLAG]);
1013 printw("%6d ", b->flagged);
1014 attrset(attrs[cc][hi][M_IDLE]);
1016 attrset(attrs[cc][0][M_FLAG]); /* We avoid the highlight intentionally */
1019 if (snip && b->o.snippets)
1022 getyx(stdscr, yy, xx);
1023 int remains = COLS-1-xx;
1027 build_snippet(snip, snip + sizeof(snip) - 1, b);
1029 if (snip[0] && remains > 2)
1031 #ifdef CONFIG_WIDE_CURSES
1032 size_t len = strlen(snip)+1;
1034 mbstowcs(snip2, snip, len);
1035 addnwstr(snip2, remains);
1037 printw("%-.*s", remains, snip);
1042 b->display_valid_until = last_scan_time + valid;
1044 attrset(attrs[0][0][M_IDLE]);
1051 cursor_max = num_mboxes;
1052 if (cursor_max > LINES-1)
1053 cursor_max = LINES-1;
1054 if (cursor_at >= cursor_max)
1055 cursor_at = cursor_max - 1;
1059 for (int i=0; i<cursor_max; i++)
1061 move(cursor_max, 0);
1064 printw("(no mailboxes found)");
1072 rethink_display(int notify)
1077 CLIST_FOR_EACH(struct mbox *, b, mboxes)
1078 if (mbox_visible_p(b))
1081 if (i >= num_mboxes || mbox_array[i] != b)
1084 if (i >= mbox_array_size)
1086 mbox_array_size = (mbox_array_size ? 2*mbox_array_size : 16);
1087 mbox_array = xrealloc(mbox_array, sizeof(struct mbox *) * mbox_array_size);
1091 if (b->o.beep && b->new > b->last_beep_new)
1093 b->last_beep_new = b->new;
1096 if (i != num_mboxes)
1106 rethink_osd(notify);
1107 if (beeeep && allow_bells && notify)
1118 intrflush(stdscr, FALSE);
1119 keypad(stdscr, TRUE);
1122 static const int attrs_mono[2][M_MAX] = {
1123 [0] = { [M_IDLE] = 0,
1128 [M_INCSEARCH] = A_REVERSE },
1129 [1] = { [M_IDLE] = 0,
1131 [M_NEW] = A_REVERSE | A_BOLD,
1132 [M_FLAG] = A_REVERSE,
1134 [M_INCSEARCH] = A_REVERSE },
1136 for (int i=0; i<2; i++)
1137 for (int j=0; j<M_MAX; j++)
1139 attrs[0][i][j] = attrs_mono[i][j];
1140 attrs[1][i][j] = attrs_mono[i][j] | A_UNDERLINE;
1141 attrs[2][i][j] = attrs_mono[i][j] | A_UNDERLINE;
1147 if (COLOR_PAIRS >= 12)
1149 init_pair(1, COLOR_YELLOW, COLOR_BLACK);
1150 init_pair(2, COLOR_RED, COLOR_BLACK);
1151 init_pair(3, COLOR_WHITE, COLOR_BLUE);
1152 init_pair(4, COLOR_YELLOW, COLOR_BLUE);
1153 init_pair(5, COLOR_RED, COLOR_BLUE);
1154 init_pair(6, COLOR_GREEN, COLOR_BLACK);
1155 init_pair(7, COLOR_GREEN, COLOR_BLUE);
1156 init_pair(8, COLOR_WHITE, COLOR_MAGENTA);
1157 init_pair(9, COLOR_YELLOW, COLOR_MAGENTA);
1158 init_pair(10, COLOR_GREEN, COLOR_MAGENTA);
1159 init_pair(11, COLOR_RED, COLOR_MAGENTA);
1160 init_pair(12, COLOR_BLACK, COLOR_YELLOW);
1161 static const int attrs_color[3][2][M_MAX] = {
1162 [0][0] = { [M_IDLE] = 0,
1163 [M_SCAN] = COLOR_PAIR(1),
1164 [M_NEW] = COLOR_PAIR(1),
1165 [M_FLAG] = COLOR_PAIR(6),
1166 [M_BAD] = COLOR_PAIR(2) },
1167 [0][1] = { [M_IDLE] = A_BOLD,
1168 [M_SCAN] = COLOR_PAIR(1),
1169 [M_NEW] = COLOR_PAIR(1) | A_BOLD,
1170 [M_FLAG] = COLOR_PAIR(6) | A_BOLD,
1171 [M_BAD] = COLOR_PAIR(2) | A_BOLD },
1172 [1][0] = { [M_IDLE] = COLOR_PAIR(3),
1173 [M_SCAN] = COLOR_PAIR(4),
1174 [M_NEW] = COLOR_PAIR(4),
1175 [M_FLAG] = COLOR_PAIR(7),
1176 [M_BAD] = COLOR_PAIR(5) },
1177 [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD,
1178 [M_SCAN] = COLOR_PAIR(4),
1179 [M_NEW] = COLOR_PAIR(4) | A_BOLD,
1180 [M_FLAG] = COLOR_PAIR(7) | A_BOLD,
1181 [M_BAD] = COLOR_PAIR(5) | A_BOLD },
1182 [2][0] = { [M_IDLE] = COLOR_PAIR(8),
1183 [M_SCAN] = COLOR_PAIR(9),
1184 [M_NEW] = COLOR_PAIR(9),
1185 [M_FLAG] = COLOR_PAIR(10),
1186 [M_BAD] = COLOR_PAIR(11),
1187 [M_INCSEARCH] = COLOR_PAIR(12) | A_DIM },
1188 [2][1] = { [M_IDLE] = COLOR_PAIR(8) | A_BOLD,
1189 [M_SCAN] = COLOR_PAIR(9),
1190 [M_NEW] = COLOR_PAIR(9) | A_BOLD,
1191 [M_FLAG] = COLOR_PAIR(10) | A_BOLD,
1192 [M_BAD] = COLOR_PAIR(11) | A_BOLD,
1193 [M_INCSEARCH] = COLOR_PAIR(12) },
1195 memcpy(attrs, attrs_color, sizeof(attrs));
1207 print_status(char *status)
1211 printw("%s", status);
1217 scan_and_redraw(int notify)
1219 print_status("Busy...");
1227 if (i >= 0 && i < cursor_max && i != cursor_at)
1229 int old = cursor_at;
1237 next_active(int since, int step)
1241 since = (since+cursor_max) % cursor_max;
1242 step = (step+cursor_max) % cursor_max;
1248 struct mbox *b = mbox_array[i];
1249 if (b->new && b->o.priority > bestp)
1252 bestp = b->o.priority;
1254 i = (i+step) % cursor_max;
1262 mbox_run(struct mbox *b)
1264 char cmd[strlen(run_cmd) + strlen(b->path) + 16];
1265 sprintf(cmd, run_cmd, b->path);
1271 b->force_refresh = 1;
1276 enter_incsearch(void)
1278 print_status("Incremental search...");
1281 redraw_line(cursor_at);
1285 handle_incsearch(int ch)
1287 if ((ch == KEY_BACKSPACE || ch == KEY_DC) && is_pos)
1289 else if (ch >= ' ' && ch <= '~')
1291 if (is_pos < sizeof(is_buf) - 1)
1292 is_buf[is_pos++] = ch;
1299 redraw_line(cursor_at);
1304 for (int i=0; i<cursor_max; i++)
1306 struct mbox *b = mbox_array[i];
1307 if (!strncmp(b->name, is_buf, is_pos))
1318 redraw_line(cursor_at);
1323 #define STR(c) STR2(c)
1328 fprintf(stderr, "Usage: cm [<options>] [<mbox-pattern> | <mbox>[=<name>]] ...\n\
1331 -c <interval>\t\tScan mailboxes every <interval> seconds (default is 30)\n\
1332 -d\t\t\tLog debug messages to stderr\n\
1333 -i\t\t\tInclude user's INBOX\n\
1334 -m <cmd>\t\tCommand to run on the selected mailbox, %%s gets replaced by mailbox path\n\
1335 -o <pattern>=<opts>\tSet mailbox options\n\
1336 -o <opts>\t\tSet default options for all mailboxes\n\
1337 -p <pri>\t\tSet minimum priority to show\n\
1338 -s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\n\
1340 Mailbox options (set with `-o', use upper case to negate):\n\
1341 0-9\t\t\tSet mailbox priority (0=default)\n\
1342 b\t\t\tBeep when a message arrives\n\
1343 d\t\t\tSend an on-screen-display message (requires OSDD)\n\
1344 e\t\t\tHide from display if empty\n\
1345 f\t\t\tShow flagged messages if there are no new ones\n\
1346 h\t\t\tHide from display\n\
1347 l<led>\t\t\tLight a keyboard led (1-9) if running on X display\n\
1348 m\t\t\tShow mailbox name of the sender\n\
1349 o\t\t\tCount old, but unread messages as new\n\
1350 p\t\t\tShow personal info (full name) of the sender\n\
1351 s\t\t\tShow message snippets\n\
1352 t\t\t\tHighlight the entry\n\
1353 !<key>\t\t\tSet hot key\n\
1355 CheckMail " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
1356 It can be freely distributed and used according to the GNU GPL v2.\n\
1362 parse_options(char *c)
1366 if (sep = strchr(c, '='))
1368 struct option_node *n = xmalloc(sizeof(*n) + sep-c);
1369 memcpy(n->pattern, c, sep-c);
1370 n->pattern[sep-c] = 0;
1371 clist_add_tail(&options, &n->n);
1377 o = &global_options;
1381 if (x >= '0' && x <= '9')
1382 o->priority = x - '0';
1383 else if (x == '!' && *c)
1385 else if (x == 'l' && *c >= '1' && *c <= '9')
1386 o->led = *c++ - '0';
1389 int value = !!islower(x);
1399 o->hide_if_empty = value;
1402 o->show_flagged = value;
1408 o->sender_mbox = value;
1411 o->unread_is_new = value;
1414 o->sender_personal = value;
1417 o->snippets = value;
1420 o->highlight = value;
1423 fprintf(stderr, "Invalid mailbox option `%c'\n", x);
1430 main(int argc, char **argv)
1432 clist_init(&mboxes);
1433 clist_init(&options);
1434 clist_init(&patterns);
1435 clist_init(&osd_opts);
1438 while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
1442 check_interval = atol(optarg);
1443 if (check_interval <= 0)
1456 parse_options(optarg);
1459 minimum_priority = atol(optarg);
1462 add_osd_opt(optarg);
1467 while (optind < argc)
1468 add_pattern(argv[optind++]);
1476 int should_exit = 0;
1478 while (!should_exit)
1480 time_t now = time(NULL);
1481 int remains = last_scan_time + check_interval - now;
1489 halfdelay((remains > 255) ? 255 : remains);
1493 if (is_active && handle_incsearch(ch))
1498 for (int i=0; i<num_mboxes; i++)
1499 if (ch == mbox_array[i]->o.hotkey)
1503 mbox_run(mbox_array[i]);
1513 move_cursor(cursor_at+1);
1517 move_cursor(cursor_at-1);
1527 move_cursor(cursor_max-1);
1530 next_active(cursor_at+1, 1);
1533 next_active(cursor_at-1, -1);
1537 if (cursor_at < cursor_max)
1538 mbox_run(mbox_array[cursor_at]);
1541 clearok(stdscr, TRUE);
1550 print_status("Bells and whistles are now enabled. Toot!");
1554 print_status("Bells and whistles are now disabled. Pssst!");
1558 print_status("On-screen display is now enabled.");
1562 print_status("On-screen display is now disabled. Watch your step.");
1568 if (ch >= '0' && ch <= '9')
1570 minimum_priority = ch - '0';
1574 debug("Pressed unknown key %d\n", ch);