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";
69 static clist options, patterns;
70 static struct options global_options = {
83 time_t display_valid_until;
84 int last_size, last_pos;
85 int total, new, flagged;
86 int last_total, last_new, last_flagged;
90 char sender_snippet[128];
91 char subject_snippet[128];
95 static struct mbox **mbox_array;
96 static int num_mboxes, mbox_array_size;
104 static clist osd_opts;
106 static void redraw_line(int i);
107 static void rethink_display(int notify);
110 add_pattern(char *patt)
112 struct pattern_node *n = xmalloc(sizeof(*n) + strlen(patt));
113 strcpy(n->pattern, patt);
114 if (patt = strchr(n->pattern, '='))
121 clist_add_tail(&patterns, &n->n);
127 struct passwd *p = getpwuid(getuid());
129 die("You don't exist, go away!");
130 char buf[sizeof("/var/mail/") + strlen(p->pw_name) + 7];
131 sprintf(buf, "/var/mail/%s=INBOX", p->pw_name);
136 init_options(struct options *o)
140 o->hide_if_empty = -1;
144 o->show_flagged = -1;
145 o->sender_personal = -1;
153 setup_options(struct mbox *b)
155 b->o = global_options;
156 CLIST_FOR_EACH(struct option_node *, n, options)
157 if (!fnmatch(n->pattern, b->name, 0))
159 debug("\tApplied options %s\n", n->pattern);
160 #define MERGE(f) if (n->o.f >= 0) b->o.f = n->o.f
163 MERGE(hide_if_empty);
168 MERGE(sender_personal);
177 add_osd_opt(char *arg)
179 struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
181 n->val = strchr(n->key, '=');
183 die("Malformed OSD option");
185 clist_add_tail(&osd_opts, &n->n);
189 mbox_name(char *path)
191 char *c = strrchr(path, '/');
192 return c ? (c+1) : path;
196 add_mbox(clist *l, char *path, char *name)
198 struct mbox *b = xmalloc(sizeof(*b));
199 bzero(b, sizeof(*b));
200 b->path = xstrdup(path);
201 b->name = xstrdup(name);
205 cnode *prev = l->head.prev;
206 while (prev != &l->head && strcmp(((struct mbox *)prev)->name, name) > 0)
208 clist_insert_after(&b->n, prev);
211 clist_add_tail(l, &b->n);
217 del_mbox(struct mbox *b)
226 find_mbox(clist *l, char *path)
228 CLIST_FOR_EACH(struct mbox *, b, *l)
229 if (!strcmp(b->path, path))
235 mbox_active_p(struct mbox *b)
237 if (b->o.priority < minimum_priority)
245 mbox_visible_p(struct mbox *b)
247 if (!mbox_active_p(b))
251 if (b->o.hide_if_empty && !b->total)
257 prepare_snippets(struct mbox *b, char *sender, char *subject)
261 while (*sender == ' ' || *sender == '\t')
263 while (*subject == ' ' || *subject == '\t')
266 pos = b->sender_snippet;
267 term = pos + sizeof(b->sender_snippet) - 1;
268 if (sender[0] && (b->o.sender_mbox || b->o.sender_personal))
269 add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
273 pos = b->subject_snippet;
274 term = pos + sizeof(b->subject_snippet) - 1;
276 add_subject_snippet(&pos, term, subject);
278 add_snippet_raw(&pos, term, "No subject");
282 build_snippet(char *buf, char *term, struct mbox *b)
284 if (b->sender_snippet[0])
286 add_snippet(&buf, term, b->sender_snippet);
287 add_snippet_raw(&buf, term, ": ");
289 add_snippet(&buf, term, b->subject_snippet);
292 static int mb_fd, mb_pos;
293 static unsigned char mb_buf[4096], *mb_cc, *mb_end;
298 mb_cc = mb_end = mb_buf;
305 lseek(mb_fd, pos, SEEK_SET);
312 return mb_pos - (mb_end - mb_cc);
318 int len = read(mb_fd, mb_buf, sizeof(mb_buf));
327 mb_end = mb_buf + len;
336 return (mb_cc < mb_end) ? *mb_cc++ : mb_ll_get();
347 mb_check(const char *p, int len)
351 if (mb_get() != *p++)
358 scan_mbox(struct mbox *b, struct stat *st)
360 char buf[1024], sender[1024], subject[1024];
363 const char from[] = "\nFrom ";
367 b->total = b->new = b->flagged = 0;
372 /* FIXME: Should we do some locking? */
374 mb_fd = open(b->path, O_RDONLY);
377 debug("[open failed: %m] ");
378 b->total = b->new = b->flagged = -1;
383 c = read(mb_fd, signature, 2);
384 lseek(mb_fd, 0, SEEK_SET);
386 if (c == 2 && !memcmp(signature, "\037\213", 2)) //gzip
388 debug("[decompressing] ");
391 die("pipe failed: %m");
394 die("fork failed: %m");
397 if (dup2(mb_fd, 0) < 0 || dup2(fds[1], 1) < 0)
398 die("dup2 failed: %m");
402 execlp("gzip", "gzip", "-cd", NULL);
403 die("Cannot execute gzip: %m");
413 if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh && !compressed)
415 mb_seek(b->last_pos);
416 if (mb_check(from, 6))
418 debug("[incremental] ");
423 debug("[incremental failed] ");
429 if (!mb_check(from+1, 5))
431 debug("[inconsistent] ");
432 b->total = b->new = b->flagged = -1;
435 b->total = b->new = b->flagged = 0;
436 b->last_total = b->last_new = b->last_flagged = 0;
437 b->snippet_is_new = 0;
441 b->total = b->last_total;
442 b->new = b->last_new;
443 b->flagged = b->last_flagged;
448 b->last_pos = mb_tell() - 5;
450 b->last_pos--; // last_pos should be the previous \n character
451 b->last_total = b->total;
452 b->last_new = b->new;
453 b->last_flagged = b->flagged;
454 while ((c = mb_get()) >= 0 && c != '\n')
469 debug("[truncated] ");
480 while (c == ' ' || c == '\t');
488 if (i < sizeof(buf) - 1)
494 if (!strncasecmp(buf, "Status:", 7))
496 else if (!strncasecmp(buf, "X-Status:", 9) && strchr(buf+9, 'F'))
498 else if (!strncasecmp(buf, "From:", 5))
499 strcpy(sender, buf+5);
500 else if (!strncasecmp(buf, "Subject:", 8))
501 strcpy(subject, buf+8);
510 debug("new=%d flagged=%d sender=<%s> subject=<%s>\n", new, flagged, sender, subject);
511 if (new || (flagged && !b->snippet_is_new))
513 b->snippet_is_new = new;
514 prepare_snippets(b, sender, subject);
533 if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
534 b->total = b->new = b->flagged = -1;
541 debug("Searching for mailboxes (notify=%d)...\n", notify);
542 last_scan_time = time(NULL);
543 CLIST_FOR_EACH(struct pattern_node *, p, patterns)
545 debug("Trying pattern %s (name %s)\n", p->pattern, p->name);
547 int err = glob(p->pattern, GLOB_ERR | GLOB_NOSORT | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &g);
548 if (err && err != GLOB_NOMATCH)
549 die("Failed to glob %s: %m", p->pattern);
550 for (uns i=0; i<g.gl_pathc; i++)
552 char *name = g.gl_pathv[i];
553 struct mbox *b = find_mbox(&mboxes, name);
556 b = add_mbox(&mboxes, name, (p->name ? p->name : mbox_name(name)));
557 debug("Discovered mailbox %s (%s)\n", b->name, b->path);
567 CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
573 debug("Lost mailbox %s\n", b->name);
580 debug("Scanning mailboxes...\n");
581 CLIST_FOR_EACH(struct mbox *, b, mboxes)
584 debug("%s: ", b->name);
585 if (!mbox_active_p(b))
591 b->force_refresh = 1;
592 if (stat(b->path, &st) < 0)
594 b->total = b->new = b->flagged = -1;
597 else if (!b->last_time || st.st_mtime != b->last_time || st.st_size != b->last_size || b->force_refresh)
600 redraw_line(b->index);
604 b->last_time = st.st_mtime;
605 b->last_size = st.st_size;
606 debug("%d %d %d (stopped at %d of %d)\n", b->total, b->new, b->flagged, b->last_pos, b->last_size);
609 redraw_line(b->index);
612 else if (b->display_valid_until <= last_scan_time)
614 debug("not changed, but needs redraw\n");
615 redraw_line(b->index);
618 debug("not changed\n");
619 b->force_refresh = 0;
623 debug("Scan finished\n");
624 last_scan_time = time(NULL);
625 rethink_display(notify);
630 #include <X11/Xlib.h>
631 #include <X11/Xatom.h>
633 static Display *x11_dpy;
634 static unsigned leds_care, leds_have, leds_want;
637 static unsigned osd_care;
638 #define OSD_MSG_SIZE 1024
639 static char osd_last_msg[OSD_MSG_SIZE];
640 static time_t osd_last_time;
645 leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
646 osd_care = (global_options.osd >= 0);
647 CLIST_FOR_EACH(struct option_node *, o, options)
650 leds_care |= (1 << o->o.led);
655 if (!leds_care && !osd_care)
657 debug("X11: No mailbox wants LEDs or OSD\n");
660 if (!getenv("DISPLAY"))
662 debug("X11: Do not have X display\n");
665 if (!(x11_dpy = XOpenDisplay(NULL)))
666 die("Cannot open X display, although the DISPLAY variable is set");
670 osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
672 die("Cannot intern OSD_QUEUE atom");
674 // If OSD options contain no message, add one
676 CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
681 add_osd_opt("=%40f");
682 add_osd_opt("=%40s");
684 add_osd_opt("=(and %m more)");
689 debug("X11: Initialized\n");
695 if (leds_want == leds_have)
698 debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
699 for (int i=1; i<10; i++)
700 if (leds_care & (leds_have ^ leds_want) & (1 << i))
704 cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
705 XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
708 leds_have = leds_want;
714 if (!leds_care || !x11_dpy)
718 CLIST_FOR_EACH(struct mbox *, b, mboxes)
719 if (b->o.led > 0 && b->new)
720 leds_want |= (1 << b->o.led);
730 format_osd_string(char *dest, char *src, struct osd_params *par)
732 char *stop = dest + OSD_MSG_SIZE - 1;
735 while (*src && dest < stop)
741 while (*src >= '0' && *src <= '9')
742 size = 10*size + *src++ - '0';
743 if (!size || size > stop-dest)
754 arg = par->mbox->sender_snippet;
757 arg = par->mbox->subject_snippet;
760 snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
763 if (par->total_new < 2)
765 snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
789 format_osd(char *msg, struct osd_params *par)
798 unsigned have_text = 0;
799 CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
801 char buf[OSD_MSG_SIZE];
802 if (!format_osd_string(buf, n->val, par))
804 if (!n->key[0] && buf[0])
806 pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf);
807 if (pos > OSD_MSG_SIZE-1)
809 pos = sprintf(msg, "OSD message too long!\n");
821 debug_osd_msg(char *msg)
825 fprintf(stderr, "OSD: <");
828 fputc((*msg != '\n' ? *msg : '|'), stderr);
831 fprintf(stderr, ">\n");
835 rethink_osd(int notify)
837 if (!osd_care || !x11_dpy || !allow_osd)
843 struct osd_params p = { .mbox = NULL, .total_new = 0 };
844 CLIST_FOR_EACH(struct mbox *, b, mboxes)
847 p.total_new += b->new;
848 if (b->new && b->last_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
852 char new_msg[OSD_MSG_SIZE];
853 format_osd(new_msg, &p);
854 debug_osd_msg(new_msg);
855 if (strcmp(new_msg, osd_last_msg))
857 strcpy(osd_last_msg, new_msg);
858 if (notify && new_msg[0])
860 debug("OSD: Sending to daemon\n");
861 XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg));
865 debug("OSD: No changes\n");
866 osd_last_time = time(NULL);
882 static void x11_init(void) { }
883 static void rethink_leds(void) { }
884 static void rethink_osd(int notify UNUSED) { }
885 static void x11_cleanup(void) { }
889 static int cursor_at, cursor_max;
891 static unsigned is_active, is_pos; // incremental search
892 static char is_buf[64];
903 static int attrs[3][2][M_MAX]; // active (2=incsearch), hilite, status
911 struct mbox *b = mbox_array[i];
912 int cc = (cursor_at == i ? (is_active ? 2 : 1) : 0);
913 int hi = b->o.highlight;
914 unsigned namepos = 0;
915 unsigned namelen = strlen(b->name);
918 attrset(attrs[cc][hi][M_IDLE]);
920 printw("%c ", b->o.hotkey);
927 attrset(attrs[cc][hi][M_INCSEARCH]);
928 for (namepos=0; namepos < is_pos && namepos < 20; namepos++)
929 addch(is_buf[namepos]);
932 attrset(attrs[cc][hi][M_NEW]);
933 else if (b->flagged && b->o.show_flagged)
934 attrset(attrs[cc][hi][M_FLAG]);
936 attrset(attrs[cc][hi][M_IDLE]);
937 while (namepos < namelen)
938 addch(b->name[namepos++]);
939 while (namepos++ < 20)
943 else if (b->scanning)
945 attrset(attrs[cc][hi][M_SCAN]);
946 printw("[SCANNING]");
948 else if (b->total < 0)
950 attrset(attrs[cc][hi][M_BAD]);
955 attrset(attrs[cc][hi][M_IDLE]);
956 printw("%6d ", b->total);
960 attrset(attrs[cc][hi][M_NEW]);
961 printw("%6d ", b->new);
962 attrset(attrs[cc][hi][M_IDLE]);
963 int age = (last_scan_time - b->last_time);
968 printw("%2d min ", age/60);
971 else if (age < 86400)
972 printw("%2d hr%c ", age/3600, (age >= 7200 ? 's' : ' '));
977 else if (b->flagged && b->o.show_flagged)
979 attrset(attrs[cc][hi][M_FLAG]);
980 printw("%6d ", b->flagged);
981 attrset(attrs[cc][hi][M_IDLE]);
983 attrset(attrs[cc][0][M_FLAG]); /* We avoid the highlight intentionally */
986 if (snip && b->o.snippets)
989 getyx(stdscr, yy, xx);
990 int remains = COLS-1-xx;
994 build_snippet(snip, snip + sizeof(snip) - 1, b);
996 if (snip[0] && remains > 2)
998 #ifdef CONFIG_WIDE_CURSES
999 size_t len = strlen(snip)+1;
1001 mbstowcs(snip2, snip, len);
1002 addnwstr(snip2, remains);
1004 printw("%-.*s", remains, snip);
1009 b->display_valid_until = last_scan_time + valid;
1011 attrset(attrs[0][0][M_IDLE]);
1018 cursor_max = num_mboxes;
1019 if (cursor_max > LINES-1)
1020 cursor_max = LINES-1;
1021 if (cursor_at >= cursor_max)
1022 cursor_at = cursor_max - 1;
1026 for (int i=0; i<cursor_max; i++)
1028 move(cursor_max, 0);
1031 printw("(no mailboxes found)");
1039 rethink_display(int notify)
1044 CLIST_FOR_EACH(struct mbox *, b, mboxes)
1045 if (mbox_visible_p(b))
1048 if (i >= num_mboxes || mbox_array[i] != b)
1051 if (i >= mbox_array_size)
1053 mbox_array_size = (mbox_array_size ? 2*mbox_array_size : 16);
1054 mbox_array = xrealloc(mbox_array, sizeof(struct mbox *) * mbox_array_size);
1058 if (b->o.beep && b->new > b->last_beep_new)
1060 b->last_beep_new = b->new;
1063 if (i != num_mboxes)
1073 rethink_osd(notify);
1074 if (beeeep && allow_bells && notify)
1085 intrflush(stdscr, FALSE);
1086 keypad(stdscr, TRUE);
1089 static const int attrs_mono[2][M_MAX] = {
1090 [0] = { [M_IDLE] = 0,
1095 [M_INCSEARCH] = A_REVERSE },
1096 [1] = { [M_IDLE] = 0,
1098 [M_NEW] = A_REVERSE | A_BOLD,
1099 [M_FLAG] = A_REVERSE,
1101 [M_INCSEARCH] = A_REVERSE },
1103 for (int i=0; i<2; i++)
1104 for (int j=0; j<M_MAX; j++)
1106 attrs[0][i][j] = attrs_mono[i][j];
1107 attrs[1][i][j] = attrs_mono[i][j] | A_UNDERLINE;
1108 attrs[2][i][j] = attrs_mono[i][j] | A_UNDERLINE;
1114 if (COLOR_PAIRS >= 12)
1116 init_pair(1, COLOR_YELLOW, COLOR_BLACK);
1117 init_pair(2, COLOR_RED, COLOR_BLACK);
1118 init_pair(3, COLOR_WHITE, COLOR_BLUE);
1119 init_pair(4, COLOR_YELLOW, COLOR_BLUE);
1120 init_pair(5, COLOR_RED, COLOR_BLUE);
1121 init_pair(6, COLOR_GREEN, COLOR_BLACK);
1122 init_pair(7, COLOR_GREEN, COLOR_BLUE);
1123 init_pair(8, COLOR_WHITE, COLOR_MAGENTA);
1124 init_pair(9, COLOR_YELLOW, COLOR_MAGENTA);
1125 init_pair(10, COLOR_GREEN, COLOR_MAGENTA);
1126 init_pair(11, COLOR_RED, COLOR_MAGENTA);
1127 init_pair(12, COLOR_BLACK, COLOR_YELLOW);
1128 static const int attrs_color[3][2][M_MAX] = {
1129 [0][0] = { [M_IDLE] = 0,
1130 [M_SCAN] = COLOR_PAIR(1),
1131 [M_NEW] = COLOR_PAIR(1),
1132 [M_FLAG] = COLOR_PAIR(6),
1133 [M_BAD] = COLOR_PAIR(2) },
1134 [0][1] = { [M_IDLE] = A_BOLD,
1135 [M_SCAN] = COLOR_PAIR(1),
1136 [M_NEW] = COLOR_PAIR(1) | A_BOLD,
1137 [M_FLAG] = COLOR_PAIR(6) | A_BOLD,
1138 [M_BAD] = COLOR_PAIR(2) | A_BOLD },
1139 [1][0] = { [M_IDLE] = COLOR_PAIR(3),
1140 [M_SCAN] = COLOR_PAIR(4),
1141 [M_NEW] = COLOR_PAIR(4),
1142 [M_FLAG] = COLOR_PAIR(7),
1143 [M_BAD] = COLOR_PAIR(5) },
1144 [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD,
1145 [M_SCAN] = COLOR_PAIR(4),
1146 [M_NEW] = COLOR_PAIR(4) | A_BOLD,
1147 [M_FLAG] = COLOR_PAIR(7) | A_BOLD,
1148 [M_BAD] = COLOR_PAIR(5) | A_BOLD },
1149 [2][0] = { [M_IDLE] = COLOR_PAIR(8),
1150 [M_SCAN] = COLOR_PAIR(9),
1151 [M_NEW] = COLOR_PAIR(9),
1152 [M_FLAG] = COLOR_PAIR(10),
1153 [M_BAD] = COLOR_PAIR(11),
1154 [M_INCSEARCH] = COLOR_PAIR(12) | A_DIM },
1155 [2][1] = { [M_IDLE] = COLOR_PAIR(8) | A_BOLD,
1156 [M_SCAN] = COLOR_PAIR(9),
1157 [M_NEW] = COLOR_PAIR(9) | A_BOLD,
1158 [M_FLAG] = COLOR_PAIR(10) | A_BOLD,
1159 [M_BAD] = COLOR_PAIR(11) | A_BOLD,
1160 [M_INCSEARCH] = COLOR_PAIR(12) },
1162 memcpy(attrs, attrs_color, sizeof(attrs));
1174 print_status(char *status)
1178 printw("%s", status);
1184 scan_and_redraw(int notify)
1186 print_status("Busy...");
1194 if (i >= 0 && i < cursor_max && i != cursor_at)
1196 int old = cursor_at;
1204 next_active(int since, int step)
1208 since = (since+cursor_max) % cursor_max;
1209 step = (step+cursor_max) % cursor_max;
1215 struct mbox *b = mbox_array[i];
1216 if (b->new && b->o.priority > bestp)
1219 bestp = b->o.priority;
1221 i = (i+step) % cursor_max;
1229 mbox_run(struct mbox *b)
1231 char cmd[strlen(run_cmd) + strlen(b->path) + 16];
1232 sprintf(cmd, run_cmd, b->path);
1238 b->force_refresh = 1;
1243 enter_incsearch(void)
1245 print_status("Incremental search...");
1248 redraw_line(cursor_at);
1252 handle_incsearch(int ch)
1254 if ((ch == KEY_BACKSPACE || ch == KEY_DC) && is_pos)
1256 else if (ch >= ' ' && ch <= '~')
1258 if (is_pos < sizeof(is_buf) - 1)
1259 is_buf[is_pos++] = ch;
1266 redraw_line(cursor_at);
1271 for (int i=0; i<cursor_max; i++)
1273 struct mbox *b = mbox_array[i];
1274 if (!strncmp(b->name, is_buf, is_pos))
1285 redraw_line(cursor_at);
1290 #define STR(c) STR2(c)
1295 fprintf(stderr, "Usage: cm [<options>] [<mbox-pattern> | <mbox>[=<name>]] ...\n\
1298 -c <interval>\t\tScan mailboxes every <interval> seconds (default is 30)\n\
1299 -d\t\t\tLog debug messages to stderr\n\
1300 -i\t\t\tInclude user's INBOX\n\
1301 -m <cmd>\t\tCommand to run on the selected mailbox, %%s gets replaced by mailbox path\n\
1302 -o <pattern>=<opts>\tSet mailbox options\n\
1303 -o <opts>\t\tSet default options for all mailboxes\n\
1304 -p <pri>\t\tSet minimum priority to show\n\
1305 -s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\n\
1307 Mailbox options (set with `-o', use upper case to negate):\n\
1308 0-9\t\t\tSet mailbox priority (0=default)\n\
1309 b\t\t\tBeep when a message arrives\n\
1310 d\t\t\tSend an on-screen-display message (requires OSDD)\n\
1311 e\t\t\tHide from display if empty\n\
1312 f\t\t\tShow flagged messages if there are no new ones\n\
1313 h\t\t\tHide from display\n\
1314 l<led>\t\t\tLight a keyboard led (1-9) if running on X display\n\
1315 m\t\t\tShow mailbox name of the sender\n\
1316 p\t\t\tShow personal info (full name) of the sender\n\
1317 s\t\t\tShow message snippets\n\
1318 t\t\t\tHighlight the entry\n\
1319 !<key>\t\t\tSet hot key\n\
1321 CheckMail " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
1322 It can be freely distributed and used according to the GNU GPL v2.\n\
1328 parse_options(char *c)
1332 if (sep = strchr(c, '='))
1334 struct option_node *n = xmalloc(sizeof(*n) + sep-c);
1335 memcpy(n->pattern, c, sep-c);
1336 n->pattern[sep-c] = 0;
1337 clist_add_tail(&options, &n->n);
1343 o = &global_options;
1347 if (x >= '0' && x <= '9')
1348 o->priority = x - '0';
1349 else if (x == '!' && *c)
1351 else if (x == 'l' && *c >= '1' && *c <= '9')
1352 o->led = *c++ - '0';
1355 int value = !!islower(x);
1365 o->hide_if_empty = value;
1368 o->show_flagged = value;
1374 o->sender_mbox = value;
1377 o->sender_personal = value;
1380 o->snippets = value;
1383 o->highlight = value;
1386 fprintf(stderr, "Invalid mailbox option `%c'\n", x);
1393 main(int argc, char **argv)
1395 clist_init(&mboxes);
1396 clist_init(&options);
1397 clist_init(&patterns);
1398 clist_init(&osd_opts);
1401 while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
1405 check_interval = atol(optarg);
1406 if (check_interval <= 0)
1419 parse_options(optarg);
1422 minimum_priority = atol(optarg);
1425 add_osd_opt(optarg);
1430 while (optind < argc)
1431 add_pattern(argv[optind++]);
1439 int should_exit = 0;
1441 while (!should_exit)
1443 time_t now = time(NULL);
1444 int remains = last_scan_time + check_interval - now;
1452 halfdelay((remains > 255) ? 255 : remains);
1456 if (is_active && handle_incsearch(ch))
1461 for (int i=0; i<num_mboxes; i++)
1462 if (ch == mbox_array[i]->o.hotkey)
1466 mbox_run(mbox_array[i]);
1476 move_cursor(cursor_at+1);
1480 move_cursor(cursor_at-1);
1490 move_cursor(cursor_max-1);
1493 next_active(cursor_at+1, 1);
1496 next_active(cursor_at-1, -1);
1500 if (cursor_at < cursor_max)
1501 mbox_run(mbox_array[cursor_at]);
1504 clearok(stdscr, TRUE);
1513 print_status("Bells and whistles are now enabled. Toot!");
1517 print_status("Bells and whistles are now disabled. Pssst!");
1521 print_status("On-screen display is now enabled.");
1525 print_status("On-screen display is now disabled. Watch your step.");
1531 if (ch >= '0' && ch <= '9')
1533 minimum_priority = ch - '0';
1537 debug("Pressed unknown key %d\n", ch);