
#define _BSD

#include<curses.h>
#include<sys/types.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<zephyr/zephyr.h>
#include<errno.h>
#include<ctype.h>

#define LINE_SIZE 256
#define BUFSIZE 5000

#define upcase(x) (islower(x) ? toupper(x) : x)

/* Magic socket functions:
   socket
   bind
   listen
   accept
   connect */

struct appt {
  struct tm when;
  char line[LINE_SIZE];
  struct appt *next;
} *first = 0;

int sock;
struct sockaddr_un sockname;
extern int errno;
char *rindex();
char username[10];

char *eatspaces (c)
char *c;
{
  while (*c == ' ') ++c;
  return c;
}

zwrite (class,instance,recip,msg) /* ZERR_NONE indicates no error */
char *class,*instance,*recip,*msg;
{
  ZNotice_t notice;

  bzero((char *) &notice, sizeof(notice));
  notice.z_kind = ACKED;
  notice.z_port = 0;
  notice.z_class = class;
  notice.z_class_inst = instance;
  notice.z_sender = 0;
  notice.z_recipient = recip;
  notice.z_default_format = "@bold(Class $class Instance $instance:)\nFrom: @bold($sender)\n$message";
  notice.z_opcode = "";
  notice.z_message = msg;
  notice.z_message_len = strlen(msg);
  if (ZSendNotice (&notice, ZAUTH) != ZERR_NONE) printf ("Error sending message\n");
  return; 
}

void zwrite_file (class,inst,recip,fname)
char *class,*inst,*recip,*fname;
{
  FILE *f;
  char buffer[BUFSIZE];
  int numread;
  
  sscanf (fname,"%s ",fname);
  if (f = fopen (fname,"r+")) {
    numread = fread (buffer,1,BUFSIZE,f);
    if (numread == BUFSIZE) numread--;
    buffer[numread] = 0;
    fclose (f);
  } 
  else 
    sprintf (buffer,"File %s not found.",fname);
  zwrite (class,inst,recip,buffer);
}

execute_program (cmd)
char *cmd;
{
  int pid,argn;
  char *args[100],ermsg[500];
  
  argn = 1;
  cmd = eatspaces (cmd);
  args[0] = cmd;
  while (*cmd) 
    if (*cmd == ' ') {
      *cmd++ = 0;
      eatspaces (cmd);
      if (*cmd)
	args[argn++] = cmd;
    } 
    else cmd++;
  args[argn] = 0;
  pid = fork ();
  if (pid == 0) {
    execv (args[0],args);
    perror ("exec");
    printf (stderr,"EXEC \"%s\" failed.",args[0]);
    exit (1);
  } else 
    if (pid > 0) 
      wait(0);
  else
    perror ("fork execute_program");
}

int number (css)
char **css;
{
  int result;

  if (isdigit(**css)) {
    result = atoi(*css);
    while (isdigit(**css))
      (*css)++;
    *css = eatspaces(*css);
    return result;
  } else 
    return 0;
}

struct appt *parse_reminder(line)
char *line;
{
  struct appt *a, *malloc();

  a = malloc(sizeof(struct appt));
  line = eatspaces(line);
  a->when.tm_hour = number(&line); 
  a->when.tm_min = number(&line);  
  a->when.tm_sec = number(&line);
  a->when.tm_mon = number(&line)-1;
  a->when.tm_mday = number(&line); 
  a->when.tm_year = number(&line); 
  if (a->when.tm_mon < 0) a->when.tm_mon = 0;
  if (a->when.tm_year>1900) a->when.tm_year -= 1900; 
  if (a->when.tm_year<80) a->when.tm_year = 80;
  strcpy (a->line,line);
  return (a);
} 

void clear_reminders ()
{
  struct appt *x,*tmp;

  x = first;
  while (x) {
    tmp = x->next;
    free (x);
    x = tmp;
  }
  first = 0;
}

static int first_day [] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 } ;

int tmvalue (t)
struct tm *t;
{
  return
    t->tm_sec+60*(t->tm_min+60*(t->tm_hour+24*(t->tm_mday+first_day[t->tm_mon]+
					       365*(t->tm_year - 80))));
}

int tmvaluenow ()
{
  long tnow;
  
  time (&tnow);
  return tmvalue(localtime(&tnow));
}

int before (t1,t2)
struct tm *t1,*t2;
{
  return (tmvalue(t1)<tmvalue(t2));
}

void add_reminder (line)
char *line;
{
  struct appt *a, *x, *prev;

  if (!(a = parse_reminder (line))) 
    return;
  a->next = 0;
  if (!first)
    first = a;
  else {
    prev = 0;
    x = first;
    while (x) {
      if (before(&a->when,&x->when)) {
	a->next = x;
	if (prev)
	  prev->next = a;
	else
	  first = a;
	return;
      }
      prev = x;
      x = x->next;
    }
    prev->next = a;
  }
}

void list_reminders ()
{
  struct appt *a;
  FILE *out;
  char *asct;
  
  a = first;
  out = stdout;
  while (a) {
    asct = asctime(&a->when);
    asct[24] = 0;
    fprintf (out,"%s: %s\n",asct,a->line);
    a = a->next;
  }
}

void execute_reminder (line)
char *line;
{
  char *arg;
  static char *class = "message";
  static char *inst = "personal";
  static char *recip;

  recip = username;
   
  arg = eatspaces(line+1);
  switch upcase(line[0]) {
  case '<':
    zwrite_file(class,inst,recip,arg);
    break;
  case '!':
    execute_program(arg);
    break;
  case 'Z':
    zwrite (class,inst,recip,arg);
    break;
  case 'M':			/* will send mail someday */
  default:
    zwrite (class,inst,recip,line);
    break;
  }
}

void load_file (fname)
char *fname;
{
}


void open_socket ()
{
  
  if ((sock = socket (AF_UNIX, SOCK_STREAM, 0)) == -1) {
    perror ("socket");
    exit (1);
  }
  sockname.sun_family = AF_UNIX;
  strcpy (sockname.sun_path, "/tmp/appts_sock");
  if (unlink(sockname.sun_path) == -1)
    if (errno != ENOENT) {
	perror ("unlink");
	exit (1);
      }
  bind (sock, &sockname, 2 + strlen(sockname.sun_path));
  if (listen (sock,1)) {
    perror ("listen");
    exit (1);
  }
}

wait_for_socket (secs_till_next_event)
int secs_till_next_event;
{
  int ns,nslength;
  fd_set rmask;
  FILE *input;
  char instr[200];
  struct sockaddr_un nsaddr;
  struct timeval timeout;
  char *arg,*nln;

  FD_ZERO (&rmask);
  FD_SET (sock, &rmask);
  timeout.tv_sec = secs_till_next_event;
  timeout.tv_usec = 0;
  if (select (sock + 1, &rmask, 0, 0, &timeout) < 0) {
    perror ("select");
    exit (1); 
  }
  if (FD_ISSET(sock, &rmask)) {
    nslength = sizeof (nsaddr);
    nsaddr.sun_family = AF_UNIX;
    ns = accept (sock, &nsaddr, &nslength); /* open socket fd */
    if (ns < 0) {
      perror ("accept");
      exit (1);
    }
    input = fdopen (ns, "r+");	/* Now, read data from file input */
    while (fgets(instr,200,input)) {
      if (nln = rindex(instr,'\n'))
	*nln = 0;
      if (instr[0] && instr[1]) {
	arg = eatspaces(instr+2);
      } else arg = 0;
      switch (instr[0]) {
      case 'l':
	switch (instr[1]) {
	case 'd':load_file (arg); break;
	case 's':
	default:list_reminders (); break;
	}
	break;
      case 'a':
	add_reminder (arg);
	break;
      case 'c':
	clear_reminders ();
	break;
      case 'e':
	execute_reminder (arg);
	break;
      case 's':
	clear_reminders ();
	fclose (input);
	exit (0);
	break;
      default:
      break;
      }
    }
    fclose (input);
  }
}

secs_till_next ()
{
  struct appt *a;
  long diff;

  while (first) {
    diff = tmvalue(&first->when) - tmvaluenow();
    if (diff <= 0) {
      a = first;
      first = a->next;
      execute_reminder (a->line);
      free (a);
    } else return diff;
  }
  return 86400;
}

void main (argc, argv)
int argc;
char **argv;
{
  int n;
  char *arg;
  int sock;
  struct sockaddr_un sockname;

  if ((sock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
    perror ("socket");
    exit (1);
  }
  sockname.sun_family = AF_UNIX;
  strcpy (sockname.sun_path, "/tmp/appts_sock");
  if (connect (sock,&sockname,2+strlen (sockname.sun_path)) >= 0) { 
    close (sock);
    printf ("apdaemon already running.  Use appts to enter appointments\n");
    exit (1);
  }
  setpgrp (0, getpgrp(getppid()));
  first = 0;
  n = 0;
  while (++n <= argc) {
    arg = argv[n];
  }
  strcpy(username,getenv("USER"));
/*   printf ("Daemon starting for user %s.\n",username); */
  open_socket ();
  switch (fork ()) {
  case 0:
/*    setpgrp (0, getpgrp(getppid())); */
    break;
  case -1:
    perror ("fork");
    break;
  default:
    exit (0);
  }
  if (ZInitialize () != ZERR_NONE)
    fprintf (stderr, "apdaemon:  WARNING:  Zephyr is broken.");
  while (1) {
    sleep (1);
    wait_for_socket (secs_till_next());
  }
}

