#include <sys/types.h>
#include <sys/time.h>
#include "direntry.h"
#include "substdio.h"
#include "sgetopt.h"
#include "strerr.h"
#include "scan.h"
#include "fmt.h"
#include "now.h"

#define FATAL "cyclog: fatal: "
#define WARNING "cyclog: warning: "
void die_usage()
{
  strerr_die1x(100,"cyclog: usage: cyclog [ -ssize ] [ -nnum ] [ -mmargin ] dir");
}

unsigned long size = 104000;
unsigned long num = 10;
unsigned long margin = 4000;

void chop()
{
  DIR *dir;
  direntry *d;
  int count;
  unsigned long stamp;
  unsigned long oldest;

  for (;;) {
    for (;;) {
      dir = opendir(".");
      if (dir) break;
      strerr_warn2(WARNING,"unable to read directory, pausing: ",&strerr_sys);
      sleep(60);
    }
  
    oldest = 0;
    count = 0;
    while (d = readdir(dir))
      if (d->d_name[0] == '@') {
        ++count;
	scan_ulong(d->d_name + 1,&stamp);
	if (!oldest || (stamp < oldest))
	  oldest = stamp;
      }
  
    closedir(dir);
  
    if (count < num) return;

    for (;;) {
      dir = opendir(".");
      if (dir) break;
      strerr_warn2(WARNING,"unable to read directory, pausing: ",&strerr_sys);
      sleep(60);
    }
  
    while (d = readdir(dir))
      if (d->d_name[0] == '@') {
	scan_ulong(d->d_name + 1,&stamp);
	if (stamp == oldest) {
	  if (unlink(d->d_name) == -1) {
            strerr_warn4(WARNING,"unable to remove ",d->d_name,", pausing: ",&strerr_sys);
	    sleep(60);
	  }
	  break;
	}
      }
  
    closedir(dir);
  }
}

char fn[20 + FMT_ULONG];

int safewrite(fd,buf,len)
int fd;
char *buf;
int len;
{
  int w;
  for (;;) {
    w = write(fd,buf,len);
    if (w > 0) return w;
    strerr_warn4(WARNING,"unable to write to ",fn,", pausing: ",&strerr_sys);
    sleep(60);
  }
}

char outbuf[1024];
substdio ssout;

int flushread(fd,buf,len) int fd; char *buf; int len;
{
  substdio_flush(&ssout);
  return read(fd,buf,len);
}

char inbuf[1024];
substdio ssin = SUBSTDIO_FDBUF(flushread,0,inbuf,sizeof inbuf);

void main(argc,argv)
int argc;
char **argv;
{
  int opt;
  char *dir;
  unsigned long bytes;
  char ch;
  int flageof;
  unsigned long lastnow;
  int fd;
  int len;

  umask(022);

  while ((opt = getopt(argc,argv,"s:n:m:")) != opteof)
    switch(opt) {
      case 's':
	scan_ulong(optarg,&size);
	if (size < 512) size = 512;
	break;
      case 'n':
	scan_ulong(optarg,&num);
	if (num < 1) num = 1;
	break;
      case 'm':
	scan_ulong(optarg,&margin);
	if (margin >= size) margin = size;
	break;
      default:
	die_usage();
    }

  argv += optind;
  dir = *argv;
  if (!dir)
    die_usage();

  if (chdir(dir) == -1)
    strerr_die4sys(111,FATAL,"unable to chdir to ",dir,": ");

  lastnow = now();
  flageof = 0;
  while (!flageof) {
    while (now() == lastnow)
      sleep(1);
    chop();

    for (;;) {
      lastnow = now();
      fn[0] = '@';
      fn[1 + fmt_uint0(fn + 1,(unsigned int) lastnow,14)] = 0;
      fd = open_excl(fn);
      if (fd != -1) break;
      strerr_warn4(WARNING,"unable to create ",fn,", pausing: ",&strerr_sys);
      sleep(60);
    }

    substdio_fdbuf(&ssout,safewrite,fd,outbuf,sizeof outbuf);
    for (bytes = size;bytes > 0;--bytes) {
      if (substdio_get(&ssin,&ch,1) < 1) { flageof = 1; break; }
      substdio_BPUTC(&ssout,ch);
      if ((ch == '\n') && (bytes <= margin)) break;
    }
    substdio_flush(&ssout);

    while (fsync(fd) == -1) {
      strerr_warn4(WARNING,"unable to sync to ",fn,", pausing: ",&strerr_sys);
      sleep(60);
    }
    fchmod(fd,0444); /* if it fails, too bad */
    close(fd);
  }

  _exit(0);
}
