#include "global.h"
#include "ctype.h"
#include "commands.h"
#include "mbuf.h"
#include "netuser.h"
#ifndef MSDOS
#include "tcp.h"
#include "usock.h"
#endif
#include "files.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: crontab.c,v 1.18 1997/08/19 01:19:22 root Exp $";
#endif

#ifdef CRONTAB

struct crontab {
	char minute[60];
	char hour[24];
	char day[31];
	char dayofweek[7];
	char month[12];
	char *cmd;
	struct crontab *next;
};
#define NULLCRONTAB ((struct crontab *)0)

static struct crontab *CRON;
static int CRONactive = -1;

extern struct cmds Cmds[];
extern const char *Days[7], *Months[12];	/*lint !e15 */
#ifdef LOCK
extern int Kblocked;
#endif


static void processcrontab (void);
static void listsegment (char *field, int size, int offset);
static void parsesegment (char *field, int size, char *val, int offset, char **tbl);
static void addline (int argc, char *argv[]);
static int docronload (int argc,char *argv[],void *p);
static int docronlist (int argc,char *argv[],void *p);
static int docronadd (int argc,char *argv[],void *p);
static int docronclear (int argc,char *argv[],void *p);
static int docrondel (int argc,char *argv[],void *p);


/* crontab subcommand table */
static struct cmds CRONtab[] = {
	{ "add",		docronadd,		0, 7, "cron add min hour day month dayofweek \"cmd\"" },
	{ "clear",		docronclear,		0, 0, NULLCHAR },
	{ "delete",		docrondel,		0, 2, "cron delete entry#" },
	{ "list",		docronlist,		0, 0, NULLCHAR },
	{ "load",		docronload,		0, 0, NULLCHAR },
	{ NULLCHAR,		NULL,			0, 0, NULLCHAR }
};


int
docrontab (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return subcmd (CRONtab, argc, argv, p);
}


static int
docronadd (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	if (argc >= 7)
		addline (--argc, ++argv);		/*lint !e608 */
	return 0;
}


static int
docrondel (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
struct crontab *cr, *tmp = NULLCRONTAB;
int i;
	i = (atoi (argv[1]) - 1);
	if (!i)	{
		cr = CRON;
		CRON = cr->next;
		free (cr->cmd);
		free (cr);
	} else	{
		for (cr = CRON; cr; cr = tmp,i--)	{
			if (i == 1)	{
				tmp = cr->next;
				cr->next = tmp->next;
				free (tmp->cmd);
				free (tmp);
				break;
			}
		}
	}
	return 0;
}


static int
docronclear (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
struct crontab *cr, *tmp;

	for (cr = CRON; cr; cr = tmp)	{
		tmp = cr->next;
		free (cr->cmd);
		free (cr);
	}
	CRON = NULLCRONTAB;
	if (argv)
		tprintf ("Crontab cleared!\n");
	return 0;
}


static int
docronload (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
FILE *fp;
char const *filename = CRONTABFile;
char buffer[1024 + 9], *cp;

	if (argc > 1)	{
		filename = argv[1];
	} else {
		(void) docronclear (1, (char **)0, (void *)0);
	}
	if ((fp = fopen (filename, READ_TEXT)) != NULLFILE)	{
		while (!feof(fp))	{
			strcpy (buffer, "cron add ");
			if (fgets (&buffer[9], 1024, fp))	{
				cp = skipwhite (&buffer[9]);
				if (*cp && *cp != '#')
					(void) cmdparse (Cmds, buffer, NULL);
			}
		}
		(void) fclose (fp);
	}
	if (argv)
		tprintf ("Crontab loaded!\n");
        return 0;
}


static int
docronlist (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
struct crontab *c;
int i = 1;

	for (c = CRON; c; c = c->next)	{
		tprintf ("%3d:  ", i++);
		listsegment (c->minute, 60, 0);
		listsegment (c->hour, 24, 0);
		listsegment (c->day, 31, 1);
		listsegment (c->month, 12, 1);
		listsegment (c->dayofweek, 7, 0);
		tprintf ("\"%s\"\n", c->cmd);
	}
	return 0;
}


/* Start up cron server */
int
cron1 (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
	if (CRONactive != -1)
		return 0;

	(void) ksignal (Curproc, 0);   /* Don't keep the parser waiting */
	chname (Curproc, "Cron server");
	CRONactive = Curproc->output;
	(void) docronload (1, (char **)0, (void *)0);

	server_disconnect_io ();
	while (CRONactive != -1)	{
		(void) kpause (60000);
		processcrontab ();
	}
	return 0;
}



/* Shut down cron server */
int
cron0 (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
	return (deleteserver (&CRONactive));
}


static void
listsegment (field, size, offset)
char *field;
int size, offset;
{
int i, k, usecomma = 0;

	/* special case check for '*' */
	for (i = 0; i < size; i++)	{
		if (!field[i])
			break;
	}
	if (i == size)
		tputc ('*');
	else	{	/* otherwise, one or more (but not ALL) values */
		for (i = 0; i < size; i++)	{
			/* Skip if this index is clear */
			if (!field[i])
				continue;

			/* We have at least ONE value here, so place a comma, if needed */
			if (usecomma)
				tputc (',');
			else
				usecomma = 1;
			
			if ((i + 1) == size || !field[i + 1])
				tprintf ("%-d", i + offset);
			else	{
				for (k = i + 1; k < size; k++)	{
					if (!field[k])
						break;
				}
				k--;
				tprintf ("%-d-%-d", i + offset, k + offset);
				i = k;
			}
		}
	}
	tputc (' ');
}


static void
parsesegment (field, size, val, offset, tbl)
char *field;
int size;
char *val;
int offset;
char **tbl;
{
char *cp = val, *temp, itwas;
int val1, val2, i, stepval;

	while (cp && *cp)	{
		itwas = 0;
		if ((temp = strpbrk (cp, "-,")) != NULLCHAR)	{
			itwas = *temp;
			*temp++ = 0;
		}
		if (*cp == '*')	{
			for (i = 0; i < size; i++)
				field[i] = 1;
		} else if (!isdigit (*cp)){
			for (i = 0; i < size; i++)	{
				if (!stricmp (tbl[i], cp))	{
					field[i] = 1;
					break;
				}
			}
		} else {
			val1 = atoi (cp);
			if (offset)
				val1 -= offset;
			if (itwas == '-' && temp)	{
				cp = temp;
				if ((temp = strpbrk (cp, ",/")) != NULLCHAR)	{
					itwas = *temp;
					*temp++ = 0;
				}
				val2 = atoi (cp);
				if (!offset)
					val2++;
				if (itwas == '/' && temp)	{
					cp = temp;
					if ((temp = strpbrk (cp, "-,")) != NULLCHAR)
						*temp++ = 0;
					stepval = atoi (cp);
				} else
					stepval = 1;
				for (i = val1; i < val2 && i < size; i += stepval)
					field[i] = 1;
			} else	{
				field[val1] = 1;
			}
		}
		cp = temp;
	}
}


static void
addline (argc, argv)
int argc;
char *argv[];
{
struct crontab *c;
int i;

	c = (struct crontab *) callocw (1, sizeof (struct crontab));
	if (c != NULLCRONTAB)	{
		parsesegment (c->minute, 60, argv[0], 0, (char **)0);
		parsesegment (c->hour, 24, argv[1], 0, (char **)0);
		parsesegment (c->day, 31, argv[2], 1, (char **)0);
		parsesegment (c->month, 12, argv[3], 1, (char **)Months);
		parsesegment (c->dayofweek, 7, argv[4], 0, (char **)Days);
		argc--;
		for (i = 5; i < argc; i++)	{
			argv[i][strlen(argv[i])] = ' ';
		}
		c->cmd = strdup (argv[5]);
		c->next = CRON;
		CRON = c;
	}
}


static void
processcrontab ()
{
struct crontab *c;
time_t now;
struct tm *tmp, t;
char *cmd;
#ifdef LOCK
int lockedstate;
#endif

	now = time ((time_t *)0);
	tmp = localtime (&now);
	/* Since calls from cmdparse CAN overwrite this temp
	   buffer, we copy the time buffer locally */
	memcpy (&t, tmp, sizeof (struct tm));
	for (c = CRON; c; c = c->next)	{
		if (!c->minute[t.tm_min])
			continue;
		if (!c->hour[t.tm_hour])
			continue;
		if (!c->day[t.tm_mday - 1])
			continue;
		if (!c->dayofweek[t.tm_wday])
			continue;
		if (!c->month[t.tm_mon])
			continue;
		cmd = _variable_expansion (strdup (c->cmd));
#ifdef LOCK
		lockedstate = Kblocked;
		Kblocked = 0;
#endif
		(void) cmdparse (Cmds, cmd, NULL);
#ifdef LOCK
		Kblocked = lockedstate;
#endif
		free (cmd);
	}
}


#endif /* CRONTAB */
