2 * Incoming Mail Checker
4 * (c) 2005--2014 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";
41 static int simple_tab;
71 static clist options, patterns;
72 static struct options global_options = {
85 time_t display_valid_until;
86 int last_size, last_pos;
87 int total, new, flagged;
88 int last_total, last_new, last_flagged;
92 char sender_snippet[128];
93 char subject_snippet[128];
97 static struct mbox **mbox_array;
98 static int num_mboxes, mbox_array_size;
100 struct osd_opt_node {
106 static clist osd_opts;
108 static void redraw_line(int i);
109 static void rethink_display(int notify);
112 add_pattern(char *patt)
114 struct pattern_node *n = xmalloc(sizeof(*n) + strlen(patt));
115 strcpy(n->pattern, patt);
116 if (patt = strchr(n->pattern, '='))
123 clist_add_tail(&patterns, &n->n);
129 struct passwd *p = getpwuid(getuid());
131 die("You don't exist, go away!");
132 char buf[sizeof("/var/mail/") + strlen(p->pw_name) + 7];
133 sprintf(buf, "/var/mail/%s=INBOX", p->pw_name);
138 init_options(struct options *o)
142 o->hide_if_empty = -1;
146 o->show_flagged = -1;
147 o->sender_personal = -1;
152 o->unread_is_new = -1;
156 setup_options(struct mbox *b)
158 b->o = global_options;
159 CLIST_FOR_EACH(struct option_node *, n, options)
160 if (!fnmatch(n->pattern, b->name, 0))
162 debug("\tApplied options %s\n", n->pattern);
163 #define MERGE(f) if (n->o.f >= 0) b->o.f = n->o.f
166 MERGE(hide_if_empty);
171 MERGE(sender_personal);
176 MERGE(unread_is_new);
181 add_osd_opt(char *arg)
183 struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
185 n->val = strchr(n->key, '=');
187 die("Malformed OSD option");
189 clist_add_tail(&osd_opts, &n->n);
193 mbox_name(char *path)
195 char *c = strrchr(path, '/');
196 return c ? (c+1) : path;
200 add_mbox(clist *l, char *path, char *name)
202 struct mbox *b = xmalloc(sizeof(*b));
203 bzero(b, sizeof(*b));
204 b->path = xstrdup(path);
205 b->name = xstrdup(name);
209 cnode *prev = l->head.prev;
210 while (prev != &l->head && strcmp(((struct mbox *)prev)->name, name) > 0)
212 clist_insert_after(&b->n, prev);
215 clist_add_tail(l, &b->n);
221 del_mbox(struct mbox *b)
230 find_mbox(clist *l, char *path)
232 CLIST_FOR_EACH(struct mbox *, b, *l)
233 if (!strcmp(b->path, path))
239 mbox_active_p(struct mbox *b)
241 if (b->o.priority < minimum_priority)
249 mbox_visible_p(struct mbox *b)
251 if (!mbox_active_p(b))
255 if (b->o.hide_if_empty && !b->total)
261 prepare_snippets(struct mbox *b, char *sender, char *subject)
265 while (*sender == ' ' || *sender == '\t')
267 while (*subject == ' ' || *subject == '\t')
270 pos = b->sender_snippet;
271 term = pos + sizeof(b->sender_snippet) - 1;
272 if (sender[0] && (b->o.sender_mbox || b->o.sender_personal))
273 add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
277 pos = b->subject_snippet;
278 term = pos + sizeof(b->subject_snippet) - 1;
280 add_subject_snippet(&pos, term, subject);
282 add_snippet_raw(&pos, term, "No subject");
286 build_snippet(char *buf, char *term, struct mbox *b)
288 if (b->sender_snippet[0])
290 add_snippet(&buf, term, b->sender_snippet);
291 add_snippet_raw(&buf, term, ": ");
293 add_snippet(&buf, term, b->subject_snippet);
296 static int mb_fd, mb_pos, mb_seekable;
297 static unsigned char mb_buf[4096], *mb_cc, *mb_end;
302 mb_cc = mb_end = mb_buf;
309 lseek(mb_fd, pos, SEEK_SET);
316 return mb_pos - (mb_end - mb_cc);
322 int len = read(mb_fd, mb_buf, sizeof(mb_buf));
331 mb_end = mb_buf + len;
340 return (mb_cc < mb_end) ? *mb_cc++ : mb_ll_get();
351 mb_check(const char *p, int len)
355 if (mb_get() != *p++)
366 int avail = mb_end - mb_cc;
369 if (mb_seekable && len >= (int) sizeof(mb_buf))
371 int next = len - len % sizeof(mb_buf);
372 mb_seek(mb_tell() + next);
382 int next = (avail < len) ? avail : len;
390 scan_mbox(struct mbox *b, struct stat *st)
392 char buf[1024], sender[1024], subject[1024];
395 const char from[] = "\nFrom ";
399 b->total = b->new = b->flagged = 0;
404 /* FIXME: Should we do some locking? */
406 mb_fd = open(b->path, O_RDONLY);
409 debug("[open failed: %m] ");
410 b->total = b->new = b->flagged = -1;
416 c = read(mb_fd, signature, 2);
417 lseek(mb_fd, 0, SEEK_SET);
419 if (c == 2 && !memcmp(signature, "\037\213", 2)) //gzip
421 debug("[decompressing] ");
424 die("pipe failed: %m");
427 die("fork failed: %m");
430 if (dup2(mb_fd, 0) < 0 || dup2(fds[1], 1) < 0)
431 die("dup2 failed: %m");
435 execlp("gzip", "gzip", "-cd", NULL);
436 die("Cannot execute gzip: %m");
447 if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh && mb_seekable)
449 mb_seek(b->last_pos);
450 if (mb_check(from, 6))
452 debug("[incremental] ");
457 debug("[incremental failed] ");
463 if (!mb_check(from+1, 5))
465 debug("[inconsistent] ");
466 b->total = b->new = b->flagged = -1;
469 b->total = b->new = b->flagged = 0;
470 b->last_total = b->last_new = b->last_flagged = 0;
471 b->snippet_is_new = 0;
475 b->total = b->last_total;
476 b->new = b->last_new;
477 b->flagged = b->last_flagged;
482 b->last_pos = mb_tell() - 5;
484 b->last_pos--; // last_pos should be the previous \n character
485 b->last_total = b->total;
486 b->last_new = b->new;
487 b->last_flagged = b->flagged;
488 while ((c = mb_get()) >= 0 && c != '\n')
491 int content_length = -1;
504 debug("[truncated] ");
517 while (c == ' ' || c == '\t');
525 if (i < sizeof(buf) - 1)
531 if (!strncasecmp(buf, "Status:", 7))
533 if (!b->o.unread_is_new || strchr(buf + 7, 'R'))
536 else if (!strncasecmp(buf, "X-Status:", 9) && strchr(buf+9, 'F'))
538 else if (!strncasecmp(buf, "From:", 5))
539 strcpy(sender, buf+5);
540 else if (!strncasecmp(buf, "Subject:", 8))
541 strcpy(subject, buf+8);
542 else if (!strncasecmp(buf, "Content-Length:", 15))
543 content_length = atoi(buf + 15);
552 debug("new=%d flagged=%d len=%d sender=<%s> subject=<%s>\n", new, flagged, content_length, sender, subject);
553 if (new || (flagged && !b->snippet_is_new))
555 b->snippet_is_new = new;
556 prepare_snippets(b, sender, subject);
559 if (content_length >= 0)
560 mb_skip(content_length);
578 if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
579 b->total = b->new = b->flagged = -1;
586 debug("Searching for mailboxes (notify=%d)...\n", notify);
587 last_scan_time = time(NULL);
588 CLIST_FOR_EACH(struct pattern_node *, p, patterns)
590 debug("Trying pattern %s (name %s)\n", p->pattern, p->name);
592 int err = glob(p->pattern, GLOB_ERR | GLOB_NOSORT | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &g);
593 if (err && err != GLOB_NOMATCH)
594 die("Failed to glob %s: %m", p->pattern);
595 for (uns i=0; i<g.gl_pathc; i++)
597 char *name = g.gl_pathv[i];
598 struct mbox *b = find_mbox(&mboxes, name);
601 b = add_mbox(&mboxes, name, (p->name ? p->name : mbox_name(name)));
602 debug("Discovered mailbox %s (%s)\n", b->name, b->path);
612 CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
618 debug("Lost mailbox %s\n", b->name);
625 debug("Scanning mailboxes...\n");
626 CLIST_FOR_EACH(struct mbox *, b, mboxes)
629 debug("%s: ", b->name);
630 if (!mbox_active_p(b))
636 b->force_refresh = 1;
637 if (stat(b->path, &st) < 0)
639 b->total = b->new = b->flagged = -1;
642 else if (!b->last_time || st.st_mtime != b->last_time || st.st_size != b->last_size || b->force_refresh)
645 redraw_line(b->index);
649 b->last_time = st.st_mtime;
650 b->last_size = st.st_size;
651 debug("%d %d %d (stopped at %d of %d)\n", b->total, b->new, b->flagged, b->last_pos, b->last_size);
654 redraw_line(b->index);
657 else if (b->display_valid_until <= last_scan_time)
659 debug("not changed, but needs redraw\n");
660 redraw_line(b->index);
663 debug("not changed\n");
664 b->force_refresh = 0;
668 debug("Scan finished\n");
669 last_scan_time = time(NULL);
670 rethink_display(notify);
675 #include <X11/Xlib.h>
676 #include <X11/Xatom.h>
678 static Display *x11_dpy;
679 static unsigned leds_care, leds_have, leds_want;
682 static unsigned osd_care;
683 #define OSD_MSG_SIZE 1024
684 static char osd_last_msg[OSD_MSG_SIZE];
685 static time_t osd_last_time;
690 leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
691 osd_care = (global_options.osd >= 0);
692 CLIST_FOR_EACH(struct option_node *, o, options)
695 leds_care |= (1 << o->o.led);
700 if (!leds_care && !osd_care)
702 debug("X11: No mailbox wants LEDs or OSD\n");
705 if (!getenv("DISPLAY"))
707 debug("X11: Do not have X display\n");
710 if (!(x11_dpy = XOpenDisplay(NULL)))
711 die("Cannot open X display, although the DISPLAY variable is set");
715 osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
717 die("Cannot intern OSD_QUEUE atom");
719 // If OSD options contain no message, add one
721 CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
726 add_osd_opt("=%40f");
727 add_osd_opt("=%40s");
729 add_osd_opt("=(and %m more)");
734 debug("X11: Initialized\n");
740 if (leds_want == leds_have)
743 debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
744 for (int i=1; i<10; i++)
745 if (leds_care & (leds_have ^ leds_want) & (1 << i))
749 cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
750 XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
753 leds_have = leds_want;
759 if (!leds_care || !x11_dpy)
763 CLIST_FOR_EACH(struct mbox *, b, mboxes)
764 if (b->o.led > 0 && b->new)
765 leds_want |= (1 << b->o.led);
775 format_osd_string(char *dest, char *src, struct osd_params *par)
777 char *stop = dest + OSD_MSG_SIZE - 1;
780 while (*src && dest < stop)
786 while (*src >= '0' && *src <= '9')
787 size = 10*size + *src++ - '0';
788 if (!size || size > stop-dest)
799 arg = par->mbox->sender_snippet;
802 arg = par->mbox->subject_snippet;
805 snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
808 if (par->total_new < 2)
810 snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
834 format_osd(char *msg, struct osd_params *par)
843 unsigned have_text = 0;
844 CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
846 char buf[OSD_MSG_SIZE];
847 if (!format_osd_string(buf, n->val, par))
849 if (!n->key[0] && buf[0])
851 pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf);
852 if (pos > OSD_MSG_SIZE-1)
854 pos = sprintf(msg, "OSD message too long!\n");
866 debug_osd_msg(char *msg)
870 fprintf(stderr, "OSD: <");
873 fputc((*msg != '\n' ? *msg : '|'), stderr);
876 fprintf(stderr, ">\n");
880 rethink_osd(int notify)
882 if (!osd_care || !x11_dpy || !allow_osd)
888 struct osd_params p = { .mbox = NULL, .total_new = 0 };
889 CLIST_FOR_EACH(struct mbox *, b, mboxes)
892 p.total_new += b->new;
893 if (b->new && b->last_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
897 char new_msg[OSD_MSG_SIZE];
898 format_osd(new_msg, &p);
899 debug_osd_msg(new_msg);
900 if (strcmp(new_msg, osd_last_msg))
902 strcpy(osd_last_msg, new_msg);
903 if (notify && new_msg[0])
905 debug("OSD: Sending to daemon\n");
906 XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg));
910 debug("OSD: No changes\n");
911 osd_last_time = time(NULL);
927 static void x11_init(void) { }
928 static void rethink_leds(void) { }
929 static void rethink_osd(int notify UNUSED) { }
930 static void x11_cleanup(void) { }
934 static int cursor_at, cursor_max;
936 static unsigned is_active, is_pos; // incremental search
937 static char is_buf[64];
948 static int attrs[3][2][M_MAX]; // active (2=incsearch), hilite, status
956 struct mbox *b = mbox_array[i];
957 int cc = (cursor_at == i ? (is_active ? 2 : 1) : 0);
958 int hi = b->o.highlight;
959 unsigned namepos = 0;
960 unsigned namelen = strlen(b->name);
963 attrset(attrs[cc][hi][M_IDLE]);
965 printw("%c ", b->o.hotkey);
972 attrset(attrs[cc][hi][M_INCSEARCH]);
973 for (namepos=0; namepos < is_pos && namepos < 20; namepos++)
974 addch(is_buf[namepos]);
977 attrset(attrs[cc][hi][M_NEW]);
978 else if (b->flagged && b->o.show_flagged)
979 attrset(attrs[cc][hi][M_FLAG]);
981 attrset(attrs[cc][hi][M_IDLE]);
982 while (namepos < namelen)
983 addch(b->name[namepos++]);
984 while (namepos++ < 20)
988 else if (b->scanning)
990 attrset(attrs[cc][hi][M_SCAN]);
991 printw("[SCANNING]");
993 else if (b->total < 0)
995 attrset(attrs[cc][hi][M_BAD]);
1000 attrset(attrs[cc][hi][M_IDLE]);
1001 printw("%6d ", b->total);
1005 attrset(attrs[cc][hi][M_NEW]);
1006 printw("%6d ", b->new);
1007 attrset(attrs[cc][hi][M_IDLE]);
1008 int age = (last_scan_time - b->last_time);
1013 printw("%2d min ", age/60);
1016 else if (age < 86400)
1017 printw("%2d hr%c ", age/3600, (age >= 7200 ? 's' : ' '));
1022 else if (b->flagged && b->o.show_flagged)
1024 attrset(attrs[cc][hi][M_FLAG]);
1025 printw("%6d ", b->flagged);
1026 attrset(attrs[cc][hi][M_IDLE]);
1028 attrset(attrs[cc][0][M_FLAG]); /* We avoid the highlight intentionally */
1031 if (snip && b->o.snippets)
1034 getyx(stdscr, yy, xx);
1035 int remains = COLS-1-xx;
1039 build_snippet(snip, snip + sizeof(snip) - 1, b);
1041 if (snip[0] && remains > 2)
1043 #ifdef CONFIG_WIDE_CURSES
1044 size_t len = strlen(snip)+1;
1046 mbstowcs(snip2, snip, len);
1047 addnwstr(snip2, remains);
1049 printw("%-.*s", remains, snip);
1054 b->display_valid_until = last_scan_time + valid;
1056 attrset(attrs[0][0][M_IDLE]);
1063 cursor_max = num_mboxes;
1064 if (cursor_max > LINES-1)
1065 cursor_max = LINES-1;
1066 if (cursor_at >= cursor_max)
1067 cursor_at = cursor_max - 1;
1071 for (int i=0; i<cursor_max; i++)
1073 move(cursor_max, 0);
1076 printw("(no mailboxes found)");
1084 rethink_display(int notify)
1089 CLIST_FOR_EACH(struct mbox *, b, mboxes)
1090 if (mbox_visible_p(b))
1093 if (i >= num_mboxes || mbox_array[i] != b)
1096 if (i >= mbox_array_size)
1098 mbox_array_size = (mbox_array_size ? 2*mbox_array_size : 16);
1099 mbox_array = xrealloc(mbox_array, sizeof(struct mbox *) * mbox_array_size);
1103 if (b->o.beep && b->new > b->last_beep_new)
1105 b->last_beep_new = b->new;
1108 if (i != num_mboxes)
1118 rethink_osd(notify);
1119 if (beeeep && allow_bells && notify)
1130 intrflush(stdscr, FALSE);
1131 keypad(stdscr, TRUE);
1134 static const int attrs_mono[2][M_MAX] = {
1135 [0] = { [M_IDLE] = 0,
1140 [M_INCSEARCH] = A_REVERSE },
1141 [1] = { [M_IDLE] = 0,
1143 [M_NEW] = A_REVERSE | A_BOLD,
1144 [M_FLAG] = A_REVERSE,
1146 [M_INCSEARCH] = A_REVERSE },
1148 for (int i=0; i<2; i++)
1149 for (int j=0; j<M_MAX; j++)
1151 attrs[0][i][j] = attrs_mono[i][j];
1152 attrs[1][i][j] = attrs_mono[i][j] | A_UNDERLINE;
1153 attrs[2][i][j] = attrs_mono[i][j] | A_UNDERLINE;
1159 if (COLOR_PAIRS >= 12)
1161 init_pair(1, COLOR_YELLOW, COLOR_BLACK);
1162 init_pair(2, COLOR_RED, COLOR_BLACK);
1163 init_pair(3, COLOR_WHITE, COLOR_BLUE);
1164 init_pair(4, COLOR_YELLOW, COLOR_BLUE);
1165 init_pair(5, COLOR_RED, COLOR_BLUE);
1166 init_pair(6, COLOR_GREEN, COLOR_BLACK);
1167 init_pair(7, COLOR_GREEN, COLOR_BLUE);
1168 init_pair(8, COLOR_WHITE, COLOR_MAGENTA);
1169 init_pair(9, COLOR_YELLOW, COLOR_MAGENTA);
1170 init_pair(10, COLOR_GREEN, COLOR_MAGENTA);
1171 init_pair(11, COLOR_RED, COLOR_MAGENTA);
1172 init_pair(12, COLOR_BLACK, COLOR_YELLOW);
1173 static const int attrs_color[3][2][M_MAX] = {
1174 [0][0] = { [M_IDLE] = 0,
1175 [M_SCAN] = COLOR_PAIR(1),
1176 [M_NEW] = COLOR_PAIR(1),
1177 [M_FLAG] = COLOR_PAIR(6),
1178 [M_BAD] = COLOR_PAIR(2) },
1179 [0][1] = { [M_IDLE] = A_BOLD,
1180 [M_SCAN] = COLOR_PAIR(1),
1181 [M_NEW] = COLOR_PAIR(1) | A_BOLD,
1182 [M_FLAG] = COLOR_PAIR(6) | A_BOLD,
1183 [M_BAD] = COLOR_PAIR(2) | A_BOLD },
1184 [1][0] = { [M_IDLE] = COLOR_PAIR(3),
1185 [M_SCAN] = COLOR_PAIR(4),
1186 [M_NEW] = COLOR_PAIR(4),
1187 [M_FLAG] = COLOR_PAIR(7),
1188 [M_BAD] = COLOR_PAIR(5) },
1189 [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD,
1190 [M_SCAN] = COLOR_PAIR(4),
1191 [M_NEW] = COLOR_PAIR(4) | A_BOLD,
1192 [M_FLAG] = COLOR_PAIR(7) | A_BOLD,
1193 [M_BAD] = COLOR_PAIR(5) | A_BOLD },
1194 [2][0] = { [M_IDLE] = COLOR_PAIR(8),
1195 [M_SCAN] = COLOR_PAIR(9),
1196 [M_NEW] = COLOR_PAIR(9),
1197 [M_FLAG] = COLOR_PAIR(10),
1198 [M_BAD] = COLOR_PAIR(11),
1199 [M_INCSEARCH] = COLOR_PAIR(12) | A_DIM },
1200 [2][1] = { [M_IDLE] = COLOR_PAIR(8) | A_BOLD,
1201 [M_SCAN] = COLOR_PAIR(9),
1202 [M_NEW] = COLOR_PAIR(9) | A_BOLD,
1203 [M_FLAG] = COLOR_PAIR(10) | A_BOLD,
1204 [M_BAD] = COLOR_PAIR(11) | A_BOLD,
1205 [M_INCSEARCH] = COLOR_PAIR(12) },
1207 memcpy(attrs, attrs_color, sizeof(attrs));
1219 print_status(char *status)
1223 printw("%s", status);
1229 scan_and_redraw(int notify)
1231 print_status("Busy...");
1239 if (i >= 0 && i < cursor_max && i != cursor_at)
1241 int old = cursor_at;
1249 next_active(int since, int step)
1253 since = (since+cursor_max) % cursor_max;
1254 step = (step+cursor_max) % cursor_max;
1260 struct mbox *b = mbox_array[i];
1271 if (b->new && b->o.priority > bestp)
1274 bestp = b->o.priority;
1277 i = (i+step) % cursor_max;
1285 mbox_run(struct mbox *b)
1287 char cmd[strlen(run_cmd) + strlen(b->path) + 16];
1288 sprintf(cmd, run_cmd, b->path);
1294 b->force_refresh = 1;
1299 enter_incsearch(void)
1301 print_status("Incremental search...");
1304 redraw_line(cursor_at);
1308 handle_incsearch(int ch)
1310 if ((ch == KEY_BACKSPACE || ch == KEY_DC) && is_pos)
1312 else if (ch >= ' ' && ch <= '~')
1314 if (is_pos < sizeof(is_buf) - 1)
1315 is_buf[is_pos++] = ch;
1322 redraw_line(cursor_at);
1327 for (int i=0; i<cursor_max; i++)
1329 struct mbox *b = mbox_array[i];
1330 if (!strncmp(b->name, is_buf, is_pos))
1341 redraw_line(cursor_at);
1346 #define STR(c) STR2(c)
1351 fprintf(stderr, "Usage: cm [<options>] [<mbox-pattern> | <mbox>[=<name>]] ...\n\
1354 -c <interval>\t\tScan mailboxes every <interval> seconds (default is 30)\n\
1355 -d\t\t\tLog debug messages to stderr\n\
1356 -i\t\t\tInclude user's INBOX\n\
1357 -m <cmd>\t\tCommand to run on the selected mailbox, %%s gets replaced by mailbox path\n\
1358 -o <pattern>=<opts>\tSet mailbox options\n\
1359 -o <opts>\t\tSet default options for all mailboxes\n\
1360 -p <pri>\t\tSet minimum priority to show\n\
1361 -s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\n\
1362 -t\t\t\tLet TAB select the next mailbox with new mail, no matter what priority it has\n\
1364 Mailbox options (set with `-o', use upper case to negate):\n\
1365 0-9\t\t\tSet mailbox priority (0=default)\n\
1366 b\t\t\tBeep when a message arrives\n\
1367 d\t\t\tSend an on-screen-display message (requires OSDD)\n\
1368 e\t\t\tHide from display if empty\n\
1369 f\t\t\tShow flagged messages if there are no new ones\n\
1370 h\t\t\tHide from display\n\
1371 l<led>\t\t\tLight a keyboard led (1-9) if running on X display\n\
1372 m\t\t\tShow mailbox name of the sender\n\
1373 o\t\t\tCount old, but unread messages as new\n\
1374 p\t\t\tShow personal info (full name) of the sender\n\
1375 s\t\t\tShow message snippets\n\
1376 t\t\t\tHighlight the entry\n\
1377 !<key>\t\t\tSet hot key\n\
1379 CheckMail " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
1380 It can be freely distributed and used according to the GNU GPL v2.\n\
1386 parse_options(char *c)
1390 if (sep = strchr(c, '='))
1392 struct option_node *n = xmalloc(sizeof(*n) + sep-c);
1393 memcpy(n->pattern, c, sep-c);
1394 n->pattern[sep-c] = 0;
1395 clist_add_tail(&options, &n->n);
1401 o = &global_options;
1405 if (x >= '0' && x <= '9')
1406 o->priority = x - '0';
1407 else if (x == '!' && *c)
1409 else if (x == 'l' && *c >= '1' && *c <= '9')
1410 o->led = *c++ - '0';
1413 int value = !!islower(x);
1423 o->hide_if_empty = value;
1426 o->show_flagged = value;
1432 o->sender_mbox = value;
1435 o->unread_is_new = value;
1438 o->sender_personal = value;
1441 o->snippets = value;
1444 o->highlight = value;
1447 fprintf(stderr, "Invalid mailbox option `%c'\n", x);
1454 main(int argc, char **argv)
1456 clist_init(&mboxes);
1457 clist_init(&options);
1458 clist_init(&patterns);
1459 clist_init(&osd_opts);
1462 while ((c = getopt(argc, argv, "c:dim:o:p:s:t")) >= 0)
1466 check_interval = atol(optarg);
1467 if (check_interval <= 0)
1480 parse_options(optarg);
1483 minimum_priority = atol(optarg);
1486 add_osd_opt(optarg);
1494 while (optind < argc)
1495 add_pattern(argv[optind++]);
1503 int should_exit = 0;
1505 while (!should_exit)
1507 time_t now = time(NULL);
1508 int remains = last_scan_time + check_interval - now;
1516 halfdelay((remains > 255) ? 255 : remains);
1520 if (is_active && handle_incsearch(ch))
1525 for (int i=0; i<num_mboxes; i++)
1526 if (ch == mbox_array[i]->o.hotkey)
1530 mbox_run(mbox_array[i]);
1540 move_cursor(cursor_at+1);
1544 move_cursor(cursor_at-1);
1554 move_cursor(cursor_max-1);
1557 next_active(cursor_at+1, 1);
1560 next_active(cursor_at-1, -1);
1564 if (cursor_at < cursor_max)
1565 mbox_run(mbox_array[cursor_at]);
1568 clearok(stdscr, TRUE);
1577 print_status("Bells and whistles are now enabled. Toot!");
1581 print_status("Bells and whistles are now disabled. Pssst!");
1585 print_status("On-screen display is now enabled.");
1589 print_status("On-screen display is now disabled. Watch your step.");
1595 if (ch >= '0' && ch <= '9')
1597 minimum_priority = ch - '0';
1601 debug("Pressed unknown key %d\n", ch);