]> mj.ucw.cz Git - checkmail.git/commitdiff
Initial revision
authorMartin Mares <mj@ucw.cz>
Fri, 20 May 2005 20:54:21 +0000 (20:54 +0000)
committerMartin Mares <mj@ucw.cz>
Fri, 20 May 2005 20:54:21 +0000 (20:54 +0000)
Makefile [new file with mode: 0644]
clists.h [new file with mode: 0644]
cm.c [new file with mode: 0644]
log [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..0c7ff67
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+DEBUG=-ggdb
+CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Winline $(DEBUG) -std=gnu99
+LDFLAGS=-lncurses
+
+all: cm
+
+cm: cm.o util.o
+
+cm.o: cm.c clists.h util.h
+util.o: util.c util.h
+
+clean:
+       rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core`
+       rm -f cm
+
+distclean: clean
+
+.PHONY: all clean distclean
diff --git a/clists.h b/clists.h
new file mode 100644 (file)
index 0000000..846752f
--- /dev/null
+++ b/clists.h
@@ -0,0 +1,93 @@
+/*
+ *     UCW Library -- Circular Linked Lists
+ *
+ *     (c) 2003--2005 Martin Mares <mj@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
+ */
+
+#ifndef _UCW_CLISTS_H
+#define _UCW_CLISTS_H
+
+typedef struct cnode {
+  struct cnode *next, *prev;
+} cnode;
+
+typedef struct clist {
+  struct cnode head;
+} clist;
+
+static inline void *clist_head(clist *l)
+{
+  return (l->head.next != &l->head) ? l->head.next : NULL;
+}
+
+static inline void *clist_tail(clist *l)
+{
+  return (l->head.prev != &l->head) ? l->head.prev : NULL;
+}
+
+static inline void *clist_next(clist *l, cnode *n)
+{
+  return (n->next != &l->head) ? (void *) n->next : NULL;
+}
+
+static inline void *clist_prev(clist *l, cnode *n)
+{
+  return (n->prev != &l->head) ? (void *) n->prev : NULL;
+}
+
+static inline int clist_empty(clist *l)
+{
+  return (l->head.next == &l->head);
+}
+
+#define CLIST_WALK(n,list) for(n=(void*)(list).head.next; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->next)
+#define CLIST_WALK_DELSAFE(n,list,tmp) for(n=(void*)(list).head.next; tmp=(void*)((cnode*)(n))->next, (cnode*)(n) != &(list).head; n=(void*)tmp)
+#define CLIST_FOR_EACH(type,n,list) for(type n=(void*)(list).head.next; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->next)
+#define CLIST_FOR_EACH_DELSAFE(type,n,list,tmp) for(type n=(void*)(list).head.next; tmp=(void*)((cnode*)(n))->next, (cnode*)(n) != &(list).head; n=(void*)tmp)
+
+static inline void clist_insert_after(cnode *what, cnode *after)
+{
+  cnode *before = after->next;
+  what->next = before;
+  what->prev = after;
+  before->prev = what;
+  after->next = what;
+}
+
+static inline void clist_insert_before(cnode *what, cnode *before)
+{
+  cnode *after = before->prev;
+  what->next = before;
+  what->prev = after;
+  before->prev = what;
+  after->next = what;
+}
+
+static inline void clist_add_tail(clist *l, cnode *n)
+{
+  clist_insert_before(n, &l->head);
+}
+
+static inline void clist_add_head(clist *l, cnode *n)
+{
+  clist_insert_after(n, &l->head);
+}
+
+static inline void clist_remove(cnode *n)
+{
+  cnode *before = n->prev;
+  cnode *after = n->next;
+  before->next = after;
+  after->prev = before;
+}
+
+static inline void clist_init(clist *l)
+{
+  cnode *head = &l->head;
+  head->next = head->prev = head;
+}
+
+#endif
diff --git a/cm.c b/cm.c
new file mode 100644 (file)
index 0000000..8b4e3e5
--- /dev/null
+++ b/cm.c
@@ -0,0 +1,669 @@
+/*
+ *     Incoming Mail Checker
+ *
+ *     (c) 2005 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <time.h>
+#include <curses.h>
+
+#include "util.h"
+#include "clists.h"
+
+static int check_interval = 30;
+static int force_refresh;
+static int lock_mboxes;
+static time_t last_scan_time;
+static char *run_cmd = "mutt -f %s";
+
+struct mbox {
+  cnode n;
+  int index;
+  int hilited;
+  int scanning;
+  int seen;
+  time_t last_time;
+  int last_size, last_pos;
+  char *name;
+  char *path;
+  int total, new;
+  int last_total, last_new;
+  int force_refresh;
+};
+
+static clist mboxes, hilites, patterns;
+static struct mbox **mbox_array;
+static int num_mboxes;
+
+static void redraw_line(int i);
+static void redraw_all(void);
+
+static char *
+mbox_name(char *path)
+{
+  char *c = strrchr(path, '/');
+  return c ? (c+1) : path;
+}
+
+static struct mbox *
+add_mbox(clist *l, char *path, char *name)
+{
+  struct mbox *b = xmalloc(sizeof(*b));
+  bzero(b, sizeof(*b));
+  b->path = xstrdup(path);
+  b->name = xstrdup(name);
+
+  if (name)
+    {
+      cnode *prev = l->head.prev;
+      while (prev != &l->head && strcmp(((struct mbox *)prev)->name, name) > 0)
+       prev = prev->prev;
+      clist_insert_after(&b->n, prev);
+    }
+  else
+    clist_add_tail(l, &b->n);
+
+  return b;
+}
+
+static void
+del_mbox(struct mbox *b)
+{
+  clist_remove(&b->n);
+  free(b->path);
+  free(b->name);
+  free(b);
+}
+
+static struct mbox *
+find_mbox(clist *l, char *path)
+{
+  CLIST_FOR_EACH(struct mbox *, b, *l)
+    if (!strcmp(b->path, path))
+      return b;
+  return NULL;
+}
+
+static void
+add_inbox(clist *l)
+{
+  struct passwd *p = getpwuid(getuid());
+  if (!p)
+    die("You don't exist, go away!");
+  char buf[sizeof("/var/mail/") + strlen(p->pw_name) + 1];
+  sprintf(buf, "/var/mail/%s", p->pw_name);
+  add_mbox(l, buf, "INBOX");
+}
+
+static int mb_fd, mb_pos;
+static unsigned char mb_buf[4096], *mb_cc, *mb_end;
+
+static void
+mb_reset(int pos)
+{
+  mb_cc = mb_end = mb_buf;
+  mb_pos = pos;
+}
+
+static int
+mb_seek(uns pos)
+{
+  lseek(mb_fd, pos, SEEK_SET);
+  mb_reset(pos);
+}
+
+static int
+mb_tell(void)
+{
+  return mb_pos - (mb_end - mb_cc);
+}
+
+static int
+mb_ll_get(void)
+{
+  int len = read(mb_fd, mb_buf, sizeof(mb_buf));
+  mb_cc = mb_buf;
+  if (len <= 0)
+    {
+      mb_end = mb_buf;
+      return -1;
+    }
+  else
+    {
+      mb_end = mb_buf + len;
+      mb_pos += len;
+      return *mb_cc++;
+    }
+}
+
+static inline int
+mb_get(void)
+{
+  return (mb_cc < mb_end) ? *mb_cc++ : mb_ll_get();
+}
+
+static int
+mb_check(const char *p, int len)
+{
+  while (len--)
+    {
+      if (mb_get() != *p++)
+       return 0;
+    }
+  return 1;
+}
+
+static void
+scan_mbox(struct mbox *b, struct stat *st)
+{
+  char buf[1024];
+  int c;
+  const char from[] = "\nFrom ";
+
+  if (!st->st_size)
+    {
+      b->total = b->new = 0;
+      b->last_pos = 0;
+      return;
+    }
+
+  /* FIXME: Locking! */
+
+  mb_fd = open(b->path, O_RDONLY);
+  if (mb_fd < 0)
+    {
+      debug("[open failed: %m] ");
+      b->total = b->new = -1;
+      return;
+    }
+  mb_reset(0);
+
+  int incremental = 0;
+  if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh)
+    {
+      mb_seek(b->last_pos);
+      if (mb_check(from, 6))
+       {
+         debug("[incremental] ");
+         incremental = 1;
+       }
+      else
+       {
+         debug("[incremental failed] ");
+         mb_seek(0);
+       }
+    }
+  if (!incremental)
+    {
+      if (!mb_check(from+1, 5))
+       {
+         debug("[inconsistent] ");
+         b->total = b->new = -1;
+         goto done;
+       }
+      b->total = b->new = 0;
+      b->last_total = b->last_new = 0;
+    }
+  else
+    {
+      b->total = b->last_total;
+      b->new = b->last_new;
+    }
+
+  for(;;)
+    {
+      b->last_pos = mb_tell() - 5;
+      if (b->last_pos)
+       b->last_pos--;          // last_pos should be the previous \n character
+      b->last_total = b->total;
+      b->last_new = b->new;
+      while ((c = mb_get()) >= 0 && c != '\n')
+       ;
+
+      int new = 1;
+      for (;;)
+       {
+         uns i = 0;
+         for (;;)
+           {
+             c = mb_get();
+             if (c < 0)
+               {
+                 debug("[truncated] ");
+                 goto done;
+               }
+             if (c == '\n')
+               break;
+             if (c == '\r')
+               continue;
+             if (i < sizeof(buf) - 1)
+               buf[i++] = c;
+           }
+         buf[i] = 0;
+         if (!buf[0])
+           break;
+         if (!strncasecmp(buf, "Status:", 7))
+           new = 0;
+       }
+
+      b->total++;
+      if (new)
+       b->new++;
+
+      int ct = 1;
+      while (from[ct])
+       {
+         c = mb_get();
+         if (c < 0)
+           goto done;
+         if (c != from[ct++])
+           ct = (c == '\n');
+       }
+    }
+
+ done:
+  close(mb_fd);
+}
+
+static void
+scan(void)
+{
+  debug("Searching for mailboxes...\n");
+  int changed = 0;
+  CLIST_FOR_EACH(struct mbox *, p, patterns)
+    {
+      glob_t g;
+      int err = glob(p->path, GLOB_ERR | GLOB_NOSORT | GLOB_TILDE | GLOB_TILDE_CHECK, NULL, &g);
+      if (err && err != GLOB_NOMATCH)
+       die("Failed to glob %s: %m", p->path);
+      for (uns i=0; i<g.gl_pathc; i++)
+       {
+         char *name = g.gl_pathv[i];
+         struct mbox *b = find_mbox(&mboxes, name);
+         if (!b)
+           {
+             b = add_mbox(&mboxes, name, (p->name ? p->name : mbox_name(name)));
+             if (find_mbox(&hilites, b->name))
+               b->hilited = 1;
+             debug("Discovered mailbox %s (%s) hilited=%d\n", b->name, b->path, b->hilited);
+             b->scanning = -1;
+             changed = 1;
+           }
+         b->seen = 1;
+       }
+      globfree(&g);
+    }
+
+  num_mboxes = 0;
+  struct mbox *tmp;
+  CLIST_FOR_EACH_DELSAFE(struct mbox *, b, mboxes, tmp)
+    {
+      if (b->seen)
+       {
+         num_mboxes++;
+         b->seen = 0;
+       }
+      else
+       {
+         debug("Lost mailbox %s\n", b->name);
+         changed = 1;
+         del_mbox(b);
+       }
+    }
+
+  if (changed)
+    {
+      debug("Reallocating mailbox array...\n");
+      free(mbox_array);
+      mbox_array = xmalloc(sizeof(struct mbox *) * (num_mboxes+1));
+      int i = 0;
+      CLIST_FOR_EACH(struct mbox *, b, mboxes)
+       {
+         b->index = i;
+         mbox_array[i++] = b;
+       }
+      redraw_all();
+      refresh();
+    }
+
+  debug("Scanning mailboxes...\n");
+  CLIST_FOR_EACH(struct mbox *, b, mboxes)
+    {
+      struct stat st;
+      debug("%s: ", b->name);
+      if (force_refresh)
+       b->force_refresh = 1;
+      if (stat(b->path, &st) < 0)
+       {
+         b->total = b->new = -1;
+         debug("%m\n");
+       }
+      else if (!b->last_time || st.st_mtime != b->last_time || st.st_size != b->last_size || b->force_refresh)
+       {
+         b->scanning = 1;
+         redraw_line(b->index);
+         refresh();
+
+         scan_mbox(b, &st);
+         b->last_time = st.st_mtime;
+         b->last_size = st.st_size;
+         debug("%d %d (stopped at %d of %d)\n", b->total, b->new, b->last_pos, b->last_size);
+
+         b->scanning = 0;
+         redraw_line(b->index);
+         refresh();
+       }
+      else
+       debug("not changed\n");
+      b->force_refresh = 0;
+    }
+  force_refresh = 0;
+
+  debug("Scan finished\n");
+  last_scan_time = time(NULL);
+}
+
+static int cursor_at, cursor_max;
+
+enum {
+  M_IDLE,
+  M_SCAN,
+  M_NEW,
+  M_BAD,
+  M_MAX
+};
+static int attrs[2][2][M_MAX];         // active, hilite, status
+
+static void
+redraw_line(int i)
+{
+  move(i, 0);
+  if (i < cursor_max)
+    {
+      struct mbox *b = mbox_array[i];
+      int cc = (cursor_at == i);
+      int hi = b->hilited;
+
+      attrset(attrs[cc][hi][M_IDLE]);
+      if (cc)
+       printw("> ");
+      else
+       printw("  ");
+      if (b->new)
+       attrset(attrs[cc][hi][M_NEW]);
+      printw("%-20s ", b->name);
+      if (b->scanning < 0)
+       ;
+      else if (b->scanning)
+       {
+         attrset(attrs[cc][hi][M_SCAN]);
+         printw("[SCANNING]");
+       }
+      else if (b->total < 0)
+       {
+         attrset(attrs[cc][hi][M_BAD]);
+         printw("BROKEN");
+       }
+      else
+       {
+         attrset(attrs[cc][hi][M_IDLE]);
+         printw("%4d ", b->total);
+         if (b->new)
+           {
+             attrset(attrs[cc][hi][M_NEW]);
+             printw("%4d", b->new);
+           }
+       }
+      attrset(attrs[cc][hi][M_IDLE]);
+    }
+  else
+    attrset(attrs[0][0][M_IDLE]);
+  clrtoeol();
+}
+
+static void
+redraw_all(void)
+{
+  cursor_max = num_mboxes;
+  if (cursor_max > LINES-1)
+    cursor_max = LINES-1;
+  if (cursor_at >= cursor_max)
+    cursor_at = cursor_max - 1;
+  if (cursor_at < 0)
+    cursor_at = 0;
+
+  for (int i=0; i<cursor_max; i++)
+    redraw_line(i);
+  move(cursor_max, 0);
+  if (!cursor_max)
+    {
+      printw("(no mailboxes found)");
+      move(1, 0);
+    }
+  clrtobot();
+}
+
+static void
+term_init(void)
+{
+  initscr();
+  cbreak();
+  noecho();
+  nonl();
+  intrflush(stdscr, FALSE);
+  keypad(stdscr, TRUE);
+  curs_set(0);
+
+  static const int attrs_mono[2][M_MAX] = {
+    [0] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_BOLD, [M_BAD] = A_DIM },
+    [1] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_REVERSE | A_BOLD, [M_BAD] = A_DIM },
+  };
+  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;
+      }
+
+  if (has_colors())
+    {
+      start_color();
+      if (COLOR_PAIRS >= 5)
+       {
+         init_pair(1, COLOR_YELLOW, COLOR_BLACK);
+         init_pair(2, COLOR_RED, COLOR_BLACK);
+         init_pair(3, COLOR_WHITE, COLOR_BLUE);
+         init_pair(4, COLOR_YELLOW, COLOR_BLUE);
+         init_pair(5, COLOR_RED, 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_BAD] = COLOR_PAIR(2) },
+           [0][1] = { [M_IDLE] = A_BOLD, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1) | 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_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_BAD] = COLOR_PAIR(5) | A_BOLD },
+         };
+         memcpy(attrs, attrs_color, sizeof(attrs));
+       }
+    }
+  clear();
+}
+
+static void
+term_cleanup(void)
+{
+  endwin();
+}
+
+static void
+scan_and_redraw(void)
+{
+  move(LINES-1, 0);
+  printw("Busy...");
+  refresh();
+  scan();
+  redraw_all();
+  refresh();
+}
+
+static void
+move_cursor(int i)
+{
+  if (i >= 0 && i < cursor_max && i != cursor_at)
+    {
+      int old = cursor_at;
+      cursor_at = i;
+      redraw_line(old);
+      redraw_line(i);
+    }
+}
+
+static void
+next_active(int since)
+{
+  if (!cursor_max)
+    return;
+  since %= cursor_max;
+  for (int pass=1; pass >= 0; pass--)
+    {
+      int i = since;
+      do {
+       if (mbox_array[i]->hilited >= pass && mbox_array[i]->new)
+         {
+           move_cursor(i);
+           return;
+         }
+       i = (i+1) % cursor_max;
+      } while (i != since);
+    }
+}
+
+static void NONRET
+usage(void)
+{
+  fprintf(stderr, "Usage: cm [<options>] <mbox-patterns>\n\
+\n\
+Options:\n\
+-c <interval>\t\tScan mailboxes every <interval> seconds (default is 30)\n\
+-d\t\t\tLog debug messages to stderr\n\
+-h <mbox>\t\tHighlight and prefer the specified mailbox\n\
+-i\t\t\tInclude user's INBOX\n\
+-l\t\t\tLock mailboxes when scanning\n\
+-m <cmd>\t\tCommand to run on the selected mailbox, %%s gets replaced by mailbox path\n\
+");
+  exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+  clist_init(&mboxes);
+  clist_init(&hilites);
+  clist_init(&patterns);
+
+  int c;
+  while ((c = getopt(argc, argv, "c:dh:ilm:")) >= 0)
+    switch (c)
+      {
+      case 'c':
+       check_interval = atol(optarg);
+       if (check_interval <= 0)
+         usage();
+       break;
+      case 'd':
+       debug_mode++;
+       break;
+      case 'h':
+       add_mbox(&hilites, optarg, NULL);
+       break;
+      case 'i':
+       add_inbox(&patterns);
+       break;
+      case 'l':
+       lock_mboxes = 1;
+       break;
+      case 'm':
+       run_cmd = optarg;
+       break;
+      default:
+       usage();
+      }
+  while (optind < argc)
+    add_mbox(&patterns, argv[optind++], NULL);
+
+  term_init();
+  scan_and_redraw();
+  next_active(0);
+
+  int should_exit = 0;
+  while (!should_exit)
+    {
+      time_t now = time(NULL);
+      int remains = last_scan_time + check_interval - now;
+      if (remains <= 0 || force_refresh)
+       scan_and_redraw();
+      else
+       {
+         remains *= 10;
+         halfdelay((remains > 255) ? 255 : remains);
+         int ch = getch();
+         switch (ch)
+           {
+           case 'q':
+             should_exit = 1;
+             break;
+           case 'j':
+           case KEY_DOWN:
+             move_cursor(cursor_at+1);
+             break;
+           case 'k':
+           case KEY_UP:
+             move_cursor(cursor_at-1);
+             break;
+           case '0':
+           case KEY_HOME:
+           case KEY_PPAGE:
+             move_cursor(0);
+             break;
+           case '$':
+           case KEY_END:
+           case KEY_NPAGE:
+             move_cursor(cursor_max-1);
+             break;
+           case '\t':
+             next_active(cursor_at+1);
+             break;
+           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();
+               }
+             break;
+           case 'r' & 0x1f:
+             force_refresh = 1;
+             break;
+           }
+         refresh();
+       }
+    }
+
+  term_cleanup();
+  return 0;
+}
diff --git a/log b/log
new file mode 100644 (file)
index 0000000..ecc2b07
--- /dev/null
+++ b/log
@@ -0,0 +1,10 @@
+./cm: invalid option -- -
+Usage: cm [<options>] <mbox-patterns>
+
+Options:
+-c <interval>          Scan mailboxes every <interval> seconds (default is 30)
+-d                     Log debug messages to stderr
+-h <mbox>              Highlight and prefer the specified mailbox
+-i                     Include user's INBOX
+-l                     Lock mailboxes when scanning
+-m <cmd>               Command to run on the selected mailbox, %s gets replaced by mailbox path
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..e211755
--- /dev/null
+++ b/util.c
@@ -0,0 +1,59 @@
+/*
+ *     Utility Functions
+ *
+ *     (c) 2005 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "util.h"
+
+int debug_mode;
+
+void NONRET
+die(char *c, ...)
+{
+  va_list args;
+  va_start(args, c);
+  fprintf(stderr, "cm: ");
+  vfprintf(stderr, c, args);
+  fputc('\n', stderr);
+  va_end(args);
+  exit(1);
+}
+
+void
+debug(char *c, ...)
+{
+  if (!debug_mode)
+    return;
+
+  va_list args;
+  va_start(args, c);
+  vfprintf(stderr, c, args);
+  fflush(stderr);
+  va_end(args);
+}
+
+void *
+xmalloc(uns size)
+{
+  void *buf = malloc(size);
+  if (!buf)
+    die("Unable to allocate %d bytes of memory", size);
+  return buf;
+}
+
+char *
+xstrdup(char *s)
+{
+  if (!s)
+    return s;
+  uns len = strlen(s) + 1;
+  char *new = xmalloc(len);
+  memcpy(new, s, len);
+  return new;
+}
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..47b19ad
--- /dev/null
+++ b/util.h
@@ -0,0 +1,26 @@
+/*
+ *     Various Utility Functions (extracted from the UCW Library)
+ *
+ *     (c) 2005 Martin Mares <mj@ucw.cz>
+ */
+
+#ifdef __GNUC__
+#define NONRET __attribute__((noreturn))
+#define UNUSED __attribute__((unused))
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+#else
+#define NONRET
+#define UNUSED
+#define likely(x) (x)
+#define ulikely(x) (x)
+#endif
+
+typedef unsigned int uns;
+
+extern int debug_mode;
+
+void NONRET die(char *x, ...);
+void debug(char *x, ...);
+void *xmalloc(uns size);
+char *xstrdup(char *s);