/* $Id: xlbc.c,v 1.2 2000/11/19 00:15:13 sra Exp $ */
/*
 * Inbox folder monitoring program for xbiff and xlbiff.
 * Rewritten in C for speed, since it gets called every 15 seconds.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>

/*
 * These are the exit codes that xbiff and xlbiff expect.
 * May as well use them internally too.
 */
#define	CODE_UP		0
#define	CODE_UNCHANGED	1
#define	CODE_DOWN	2

static int checkdir(char *marker, int update)
{
  struct stat st_last, st_mark;
  char filename[sizeof("4294967295")];
  struct dirent *dp;
  unsigned long last = 0;
  DIR *dd;

  if ((dd = opendir(".")) == 0) {
    perror("opendir()");
    return CODE_UNCHANGED;
  }

  while ((dp = readdir(dd)) != 0) {
    unsigned long cur = 0;
    char *cp = dp->d_name;

    if (*cp != '0')
      while (*cp >= '0' && *cp <= '9')
	cur += *cp++ - '0';

    if (*cp == '\0' && cur > last)
      last = cur;
  }

  closedir(dd);

  /*
   * No new mail, lower flag.
   */
  if (!last)
    return CODE_DOWN;

  sprintf(filename, "%lu", last);

  if (stat(filename, &st_last) < 0) {
    /*
     * Timing screw, since this file was here a second ago.
     * Just claim that there's been no change, we'll try
     * again later.
     */
    return CODE_UNCHANGED;
  }

  if (stat(marker, &st_mark) < 0) {
    /*
     * Marker file doesn't exist, so create it.
     * This case should always return CODE_UP.
     */
    int fd;
    if (!update)
      return CODE_UP;
    if ((fd = open(marker, O_WRONLY | O_CREAT, 0600)) < 0)
      return CODE_UNCHANGED;
    if (fstat(fd, &st_mark) < 0)
      perror("fstat()");
    close(fd);
  }
  else {
    /*
     * Got valid stat() of each file.  Compare as timevals rather than
     * timespecs to avoid round-off error (which shows up as multiple
     * CODE_UP events for a single new message).
     */
     struct timeval tv_last, tv_mark;
     TIMESPEC_TO_TIMEVAL(&tv_mark, &st_mark.st_mtimespec);
     TIMESPEC_TO_TIMEVAL(&tv_last, &st_last.st_mtimespec);

     if (tv_last.tv_sec < tv_mark.tv_sec ||
	 (tv_last.tv_sec == tv_mark.tv_sec &&
	  tv_last.tv_usec <= tv_mark.tv_usec))
       /*
	* Last message no newer than marker.
	*/
       return CODE_UNCHANGED;
  }

  /*
   * Last message is newer than marker, so update marker
   * and raise the flag.
   */
  if (update) {
    struct timeval tv[2];
    TIMESPEC_TO_TIMEVAL(&tv[0], &st_mark.st_atimespec);
    TIMESPEC_TO_TIMEVAL(&tv[1], &st_last.st_mtimespec);
    if (utimes(marker, tv) < 0)
      perror("utimes()");
  }

  return CODE_UP;
}


int main(int argc, char *argv[])
{
  int i, home, code, result = CODE_DOWN, update = !getenv("XLBC_NOUPDATE");
  char *display = getenv("DISPLAY"), marker[256];

  strcpy(marker, ".marker.");

  if (display)
    strcat(marker, display);
  else
    fprintf(stderr, "$DISPLAY not set\n");

  if ((home = open(".", O_RDONLY, 0777)) < 0)
    perror("open(\".\")");
  
  for (i = 1; i < argc; i++) {
    if (chdir(argv[i]) < 0) {
      perror("chdir()");
      continue;
    }
    if ((code = checkdir(marker, update)) != CODE_DOWN)
      puts(argv[i]);
    if (code < result)
      result = code;
    if (fchdir(home) < 0)
      perror("fchdir()");
  }

  return result;
}

