#include <\fido\fido.h>	/* Fido v12's structures */

/*
	Generate a LASTUSR.BBS file by pulling out
	the correct record number from CALLER.SYS.


	Tom Jennings 15 Feb 90
	(k) All rights reversed

*/

/* Version 11 callers record (modified). */

#define OLDMAXLREAD 10

struct  _usr {
	char name[36];		/* users ASCII name, */
	char city[36];		/* city and state, */

/* This structure takes exactly 40 bytes, and replaces the old 
first date. (Which was luckily int[20] by mistake!) */

	struct {
		int area;	/* message area */
		int msg;	/* last msg read */
	} lastmsg[OLDMAXLREAD];

	char pwd[16];		/* what else, */
	int times;		/* # times called, */
	int help;		/* last help setting, */
	int tabs;		/* 1 == expand tabs, */
	int nulls;		/* number of nulls after CR, */
	int msg;		/* last selected message area, */
	int more;		/* last MORE setting, */
	int priv;		/* user privelege level, */
	char ldate[20];		/* last time called, */
	int time;		/* total time on system in 1 day, */
	unsigned bits;		/* various bit flags, */
	unsigned upld;		/* total K byte uploaded, */
	unsigned dnld;		/* total K bytes downloaded, */
	unsigned dnldl;		/* download, for limiting, */
	int files;		/* last selected file area, */
	char width;		/* screen width, */
	char len;		/* screen length, */
	int credit;		/* credit, in cents, */
	int debit;		/* debit, in cents, */

/* These are NOT part of the Fido 11 caller record; they were added simply to
provide the extra info needed for various utilities. */

	int next_event;		/* time til next event (0 - 1440 mins) or -1 */
	unsigned baud;		/* callers last baud rate */
	unsigned extra[16];	/* oh, something for later */
};

struct _fido fido;		/* v12 FIDO.SYS structure */
struct _usr user;		/* v11 USER.BBS record structure */
struct _clr caller;		/* v12 CALLER.SYS record structure */
char taskid;			/* program taskID */

int _stack = 4000;


main(argc,argv)
int argc;
char **argv;
{
int f,i,n;
char *cp,buff[80],swbuff[80];
long p;

	printf("Generating Fido/FidoNet version 11 \"LASTUSER.BBS\"\r\n");
	printf("Tom Jennings 18 Feb 90, (k) all rites reversed\r\n");

	allmem();				/* remove these lines */
	fastfile(2);				/* remove these lines */

	while (--argc) {
		cpyarg(buff,*++argv);		/* filename? */
		strip_switch(swbuff,*argv);	/* the switches, */
		cp= swbuff;
		while (*cp) {
			switch (*cp) {
				case 'I': taskid= atoi(buff); 
					cprintf(" * TaskID: %d\r\n",taskid); 
					break;
				default: printf("\07\"/%c\" doesn't mean anything\r\n",*cp); break;
			}
			++cp;
		}
	}
	f= open("fido.sys",0);			/* read FIDO.SYS */
	if (f == -1) {
		printf("Can't find \"FIDO.SYS\"!\r\n");
		exit(1);
	}
	read(f,&fido,sizeof(struct _fido));	/* too lazy to error check */
	close(f);
	if (fido.fido_version != FIDOVER) {
		printf("\"FIDO.SYS\" version doesn't match\r\n");
		exit(1);
	}
	f= open("caller.sys",0);		/* then read CALLER.SYS */
	if (f == -1) {
		printf("Can't find \"CALLER.SYS\"!\r\n");
		exit(1);
	}					/* now yank the right record */
	p= fido.caller;				/* explicit arithmetic */
	p *= sizeof(struct _clr);		/* avoid lattice 2.15 bug */
	lseek(f,p,0);
	read(f,&caller,sizeof(struct _clr));	/* too lazy to error check */
	close(f);

	for (cp= (char *)&user, f= sizeof(struct _usr); f--;) *cp++= 0;

	strcpy(user.name,caller.name);		/* copy some fields as is */
	strcpy(user.city,caller.city);
	strcpy(user.pwd,caller.pwd);
	strcpy(user.ldate,caller.date);

	for (f= 0; f < OLDMAXLREAD; f++) {	/* last read msg stuff */
		if (caller.lastmsg[f].area != 0) {
			user.lastmsg[f].area= caller.lastmsg[f].area + 1;
			user.lastmsg[f].msg= caller.lastmsg[f].msg;
		}
	}
	switch (uval(22,7)) {			/* translate priv */
		case 0: user.priv= -1; break;	/* TWIT */
		case 2: user.priv= 2; break;	/* NORMAL */
		case 3: user.priv= 4; break;	/* PRIVEL */
		case 4: user.priv= 6; break;	/* EXTRA */
		case 7: user.priv= 10; break;	/* SYSOP */
		case 1: 
		default:
			user.priv= 0; break;	/* DISGRACE */
	}
	switch (uval(2,3)) {			/* translate help */
		case 0: user.help= 2; break;
		case 1: user.help= 4; break;
		case 2: 
		default: user.help= 6; break;
	}
	user.width= uval(8,127);
	user.len= uval(15,127);
	user.nulls= uval(4,15);
	user.more= uval(1L,1);
	user.tabs= uval(1,1);

	user.msg= caller.msg;
	user.files= caller.files;
	user.time= caller.time;
	user.times= caller.time;
	user.dnld= caller.dnld;
	user.dnldl= caller.dnldl;
	user.upld= caller.upld;
	user.credit= caller.credit;
	user.debit= caller.debit;

/* Now fill in the extra, non-fido-11 stuff. */

	user.next_event= til_sched();		/* time til next event */
	user.baud= caller.baud;			/* last-used baud rate */

	f= creat("lastuser.bbs",1);		/* create LASTUSR.BBS */
	if (f == -1) {
		cprintf("Can't create \"LASTUSER.BBS\"!\r\n");
		exit(1);
	}
	write(f,&user,sizeof(struct _usr));
	close(f);
	exit(0);
}

/* 
til_sched();
		Returns the number of minutes until the soonest
		runnable event, or -1 if none.

		OPTional events return as -1, until til_sched
		is called during the event itself, ie. 0 minutes til
		runtime. This prevents OPT events from preempting other
		events; they are, after all, optional.

		Completed events return -1. 
		
		TaskIDs must match; events with not-our task ID are 
		considered to not exist, ie. return as -2.
*/

til_sched() {
int n,i;
unsigned next_time;
int next_event;
struct _sched *sc;

	next_time= MINS_WK;				/* oldest possible */
	next_event= -1;					/* none of them */

	for (i= 0; i < SCHEDS; i++) {
		sc= &fido.sched[i];			/* keep it simple! */
		if (! sc-> tag) break;			/* NUL == end of table */

		if (sc-> tag == 'Y') continue;		/* ignore P)age */
		if (sc-> tag == 'Z') continue;		/* ignore F)ilerequest */

/* If this is not for our task ID, then ignore it. */

		if (sc-> taskid && (sc-> taskid != taskid))
			continue;			/* only our task ID */

/* If there is a switch specified for this event, make sure it is on, otherwise
ignore this event. */

		if (sc-> swtch) 			/* if a switch is specified, */
			if (! istoggle(sc-> swtch - 1)) 
				continue;		/* it must be on */

/* OK, it's runnable. Get the time til this event runs. */

		n= til_event(i);			/* time til this event runs, */

/* If we are in the middle of this events window, (time til event == 0)
see if it has already run (SCHED_COMPLETE); if so, ignore it. */

		if (n == 0) {				/* if its runnable NOW, */
			if (sc-> bits & SCHED_COMPLETE) continue;
			next_event= i;
			next_time= n;			/* not run yet */
		}

/* Remember the next-soonest event, so we can return it when we terminate the
loop. OPTIONAL events are not checked, since are only interested in them
when their time comes up. */

		if ((n < next_time) && !(sc-> bits & SCHED_OPTIONAL)) {
			next_event= i;			/* this is soonest */
			next_time= n;			/* so far, */
		}
/* If the soonest is in "zero minutes" (ie now!) then there's no sense in
looking any further. */

		if (! next_time) break;
	}

/* If we found one within the desired time, return it, else return -1. */

	if (next_event != -1) return(next_time);	/* found one */
	else return(-1);				/* else we didnt */
}

/* Return the number of minutes until this event can be run, or 0 if it
should be running now. */

til_event(n)
int n;
{
char daywk,d,h,m;
int now,start,end;

	if (n >= SCHEDS) return(-1);		/* out of range */

	get_time(&d,&h,&m);			/* get current time, */
	now= lt(d,h,m);				/* make mins since Sun 00:00 */

	daywk= fido.sched[n].daywk;		/* convert event day "ALL" */
	if (daywk > 6) daywk= d;		/* into "today" */
	start= lt(daywk,fido.sched[n].hr,fido.sched[n].min); /* when it starts */

	end= start + fido.sched[n].len;		/* when it ends */

	if ((now >= start) && (now < end)) 	/* should be running now */
		return(0);			/* zero mins until start ... */

	start -= now;				/* just time until it starts */
	if (start < 0) start += MINS_WK;	/* modulo one week */
	if (fido.sched[n].daywk > 6) 		/* if day == ALL */
		start %= MINS_DAY;		/* 24 * 60 or less, no? */
	return(start);
}

/* Convert the time elements dayinweek, hour, minute to an integer minutes 
since Sun. 00:00. */

lt(d,h,m)
char d,h,m;
{
	return((d * MINS_DAY) + (h * MINS_HR) + m);
}

/* Get the time of day, hours & minutes. Make sure we dont catch in changing. */

get_time(d,h,m)
char *d,*h,*m;
{
char dt,ht,mt;
int n;

	for (n= 0; n < 10; n++) {
		*d= gtod3(3); *h= gtod3(4); *m= gtod3(5);
		dt= gtod3(3); ht= gtod3(4); mt= gtod3(5);
		if ((*d == dt) && (*h == ht) && (*m == mt)) return;
	}
	cprintf("Clock keeps changing? (get_time)\r\n");
}

/* Return toggle #N. */

istoggle(n)
int n;
{
	n= 1 << n;				/* it's easier to type */
	if (fido.oneshots & n) return(-1);	/* oneshot set, (ONCE) */
	return((fido.switches & n) ? 1 : 0);	/* switch (ON) or (OFF) */
}

/* Strip any switches from the input string, put into the output array. */

strip_switch(out,in)
char *out;
char *in;
{
	while (*in && (*in != '/'))		/* skip to end of string */
		++in;				/* or first switch, */

	while (*in && (*in == '/')) {		/* copy switch args while */
		++in;
		*out++ = toupper(*in);		/* stripping switch chars, */
		++in;
	}
	*out= '\0';				/* terminate it, */
}

/* Copy the string to the destination array, stopping if we find one
of our delimiters or switches. */

cpyarg(to,from)
char *to;
char *from;
{
	while ((*from != ' ') && (*from != '/') && *from) 
		*to++= *from++;
	*to= '\0';
}
/* Return the value from the caller bit record. Since there is no equate
or other sensible system for the resolution of 4 used in CLR_WID, we
hard code the value here, since its pretty much hardcoded anyways. */

uval(shift,mask)
int shift,mask;
{
int n;

	n= (caller.stuff >> shift) & mask;
	return(n);
}

/* Set a field in the caller bit record. */

setuval(val,mask,shift)
int val,mask,shift;
{
long n;

	n= mask; n <<= shift;			/* we NEED long arith */
	caller.stuff &= ~n;			/* remove old value */

	n= val & mask; n <<= shift;		/* set it THEN shift it */
	caller.stuff |= n;			/* add in new value */
}

