/*
 * at time mon day
 * at time wday
 * at time wday 'week'
 *
 */
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <signal.h>

#define HOUR 100
#define HALFDAY	(12*HOUR)
#define DAY	(24*HOUR)
#define THISDAY "/usr/spool/at"

char *days[] = {
	"sunday",
	"monday",
	"tuesday",
	"wednesday",
	"thursday",
	"friday",
	"saturday",
};

struct monstr {
	char *mname; 
	int mlen;
} months[] = {
	{ "january", 31 },
	{ "february", 28 },
	{ "march", 31 },
	{ "april", 30 },
	{ "may", 31 },
	{ "june", 30 },
	{ "july", 31 },
	{ "august", 31 },
	{ "september", 30 },
	{ "october", 31 },
	{ "november", 30 },
	{ "december", 31 },
	{ 0, 0 },
};

char	fname[100];
int	utime;  /* requested time in grains */
int	now;	/* when is it */
int	uday; /* day of year to be done */
int	uyear; /* year */
int	today; /* day of year today */
FILE	*file;
FILE	*ifile;
extern char	**environ;		/***/
char	*prefix();
FILE	*popen();

main(argc, argv)
char **argv;
{
	extern onintr();
	register c;
	char pwbuf[100];
	FILE *pwfil;
	int larg;

	/* argv[1] is the user's time: e.g.,  3AM */
	/* argv[2] is a month name or day of week */
	/* argv[3] is day of month or 'week' */
	/* another argument might be an input file */
	if (argc < 2) {
		fprintf(stderr, "at: arg count\n");
		exit(1);
	}
	makeutime(argv[1]);
	larg = makeuday(argc,argv)+1;
	if (uday==today && larg<=2 && utime<=now)
		uday++;
	c = uyear%4==0? 366: 365;
	if (uday >= c) {
		uday -= c;
		uyear++;
	}
	filename(THISDAY, uyear, uday, utime);
	ifile = stdin;
	if (argc > larg)
		ifile = fopen(argv[larg], "r");
	if (ifile == NULL) {
		fprintf(stderr, "at: cannot open input: %s\n", argv[larg]);
		exit(1);
	}
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, onintr);
	file = fopen(fname, "a");
	chmod(fname, 0644);
	if (file == NULL) {
		fprintf(stderr, "at: cannot open memo file\n");
		exit(1);
	}
	if ((pwfil = popen("pwd", "r")) == NULL) {
		fprintf(stderr, "at: can't execute pwd\n");
		exit(1);
	}
	fgets(pwbuf, 100, pwfil);
	pclose(pwfil);
	fprintf(file, "cd %s", pwbuf);
	if (environ) {
		char **ep = environ;
		while(*ep)
			fprintf(file, "%s\n", *ep++);
	}
	while((c = getc(ifile)) != EOF) {
		putc(c, file);
	}
	exit(0);
}

makeutime(pp)
char *pp; 
{
	register val;
	register char *p;

	/* p points to a user time */
	p = pp;
	val = 0;
	while(isdigit(*p)) {
		val = val*10+(*p++ -'0');
	}
	if (p-pp < 3)
		val *= HOUR;

	for (;;) {
		switch(*p) {

		case ':':
			++p;
			if (isdigit(*p)) {
				if (isdigit(p[1])) {
					val +=(10* *p + p[1] - 11*'0');
					p += 2;
					continue;
				}
			}
			fprintf(stderr, "at: bad time format:\n");
			exit(1);

		case 'A':
		case 'a':
			if (val >= HALFDAY+HOUR)
				val = DAY+1;  /* illegal */
			if (val >= HALFDAY && val <(HALFDAY+HOUR))
				val -= HALFDAY;
			break;

		case 'P':
		case 'p':
			if (val >= HALFDAY+HOUR)
				val = DAY+1;  /* illegal */
			if (val < HALFDAY)
				val += HALFDAY;
			break;

		case 'n':
		case 'N':
			val = HALFDAY;
			break;

		case 'M':
		case 'm':
			val = 0;
			break;


		case '\0':
		case ' ':
			/* 24 hour time */
			if (val == DAY)
				val -= DAY;
			break;

		default:
			fprintf(stderr, "at: bad time format\n");
			exit(1);

		}
		break;
	}
	if (val < 0 || val >= DAY) {
		fprintf(stderr, "at: time out of range\n");
		exit(1);
	}
	if (val%HOUR >= 60) {
		fprintf(stderr, "at: illegal minute field\n");
		exit(1);
	}
	utime = val;
}


makeuday(argc,argv)
char **argv;
{
	/* the presumption is that argv[2], argv[3] are either
	   month day OR weekday [week].  Returns either 2 or 3 as last
	   argument used */
	/* first of all, what's today */
	long tm;
	int found = -1;
	char **ps;
	struct tm *detail, *localtime();
	struct monstr *pt;

	time(&tm);
	detail = localtime(&tm);
	uday = today = detail->tm_yday;
	uyear = detail->tm_year;
	now = detail->tm_hour*100+detail->tm_min;
	if (argc<=2)
		return(1);
	/* is the next argument a month name ? */
	for (pt=months; pt->mname; pt++) {
		if (prefix(argv[2], pt->mname)) {
			if (found<0)
				found = pt-months;
			else {
				fprintf(stderr, "at: ambiguous month\n");
				exit(1);
			}
		}
	}
	if (found>=0) {
		if (argc<=3)
			return(2);
		uday = atoi(argv[3]) - 1;
		if (uday<0) {
			fprintf(stderr, "at: illegal day\n");
			exit(1);
		}
		while(--found>=0)
			uday += months[found].mlen;
		if (detail->tm_year%4==0 && uday>59)
			uday += 1;
		return(3);
	}
	/* not a month, try day of week */
	found = -1;
	for (ps=days; ps<days+7; ps++) {
		if (prefix(argv[2], *ps)) {
			if (found<0)
				found = ps-days;
			else {
				fprintf(stderr, "at: ambiguous day of week\n");
				exit(1);
			}
		}
	}
	if (found<0)
		return(1);
	/* find next day of this sort */
	uday = found - detail->tm_wday;
	if (uday<=0)
		uday += 7;
	uday += today;
	if (argc>3 && strcmp("week", argv[3])==0) {
		uday += 7;
		return(3);
	}
	return(2);
}

char *
prefix(begin, full)
char *begin, *full;
{
	int c;
	while (c = *begin++) {
		if (isupper(c))
			c = tolower(c);
		if (*full != c)
			return(0);
		else
			full++;
	}
	return(full);
}

filename(dir, y, d, t)
char *dir;
{
	register i;

	for (i=0; ; i += 53) {
		sprintf(fname, "%s/%02d.%03d.%04d.%02d", dir, y, d, t,
		   (getpid()+i)%100);
		if (access(fname, 0) == -1)
			return;
	}
}

onintr()
{
	unlink(fname);
	exit(1);
}
