#include "prjlibs-include/standards.h"
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>

#include "skalibs/include/stddjb.h"
#include "prjlibs-include/constants.h"
#include "runwhen.h"

char const* PROG="rw-wake";

static void alarm_handler(int sig) { (void)sig; }

int main(int argc, char** argv) {
  struct itimerval timer;
  struct sigaction action;
  sigset_t alarmset;
  sigset_t blockset;
  int flag_sleep;

  if (argc<2)
    strerr_die3x(100, usage, PROG, " {stamp|-} program [arg...]");

  /* set up sigset containing SIGALRM */
  if (sigemptyset(&alarmset)!=0)
    strerr_die5sys(100, PROG, err_unable, err_empty, err_sigset, ": ");
  if (sigaddset(&alarmset, SIGALRM)!=0)
    strerr_die5sys(100, PROG, err_unable, err_addto, err_sigset, ": ");

  /* block SIGALRM ASAP */
  if (sigprocmask(SIG_BLOCK, &alarmset, &blockset)!=0)
    strerr_die4sys(100, PROG, err_unable, err_sigmask, ": ");

  /* figure out how long to sleep */
  if (argv[1][0]=='-' && argv[1][1]=='\0') {
    flag_sleep=1;
    timer.it_value.tv_sec=0;
    timer.it_value.tv_usec=0;
  } else {
    struct taia now, wake_time;
    taia_now(&now);
    rw_scan(&wake_time, argv[1]);
    if (taia_less(&wake_time, &now)) flag_sleep=0;
    else {
      taia_sub(&wake_time, &wake_time, &now);
      if (timeval_from_taia_relative(&timer.it_value, &wake_time)==0)
        strerr_die4x(100, PROG, ": sleep time", err_oflow, "timeval");
      flag_sleep=1;
    }
  }

  /* initialize SIGALRM action, for catching or ignoring later */
  if (sigemptyset(&action.sa_mask)!=0)
    strerr_die5sys(100, PROG, err_unable, err_empty, err_sigset, ": ");
  action.sa_flags=0;

  if (flag_sleep) {
    /* schedule SIGALRM */
    timer.it_interval.tv_sec=0;
    timer.it_interval.tv_usec=0;
    if (setitimer(ITIMER_REAL, &timer, null)!=0)
      strerr_die4sys(100, PROG, err_unable, err_timer, ": ");

    /* catch SIGALRM */
    action.sa_handler=alarm_handler;
    if (sigaction(SIGALRM, &action, null)!=0)
      strerr_die4sys(100, PROG, err_unable, err_sigact, ": ");

    /* sleep */
    if (sigdelset(&blockset, SIGALRM)!=0)
      strerr_die5sys(100, PROG, err_unable, err_delfrom, err_sigset, ": ");
    if (sigsuspend(&blockset)!=0 && errno!=EINTR)
      strerr_die4sys(100, PROG, err_unable, err_suspend, ": ");
  }

  /* ignore and unblock SIGALRM */
  action.sa_handler=SIG_IGN;
  if (sigaction(SIGALRM, &action, null)!=0)
    strerr_die4sys(100, PROG, err_unable, err_sigact, ": ");
  if (sigprocmask(SIG_UNBLOCK, &alarmset, null)!=0)
    strerr_die4sys(100, PROG, err_unable, err_sigmask, ": ");

  argv+=2;
  pathexec0_run((char const**)argv, (char const**)environ);
  strerr_die5sys(errstat, PROG, err_unable, err_exec, argv[0], ": ");
}


syntax highlighted by Code2HTML, v. 0.9.1