/* #define TEST */
#ifdef TEST
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#else
#include "global.h"
#include "commands.h"
#endif
#ifndef MSDOS
#include <time.h>
#endif

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


#define OUTSIDE_BBS	0
#define INSIDE_BBS	1
#define FWD_SCRIPT	2
#define FWD_AREAS	3


extern int FWDmode, FWDminidle, FWDlimittime;

#ifdef TEST
#include "fwdparse.h"
#define Forwardfile "new.bbs"
#define NULLFILE ((FILE *)0)
#define NULLCHAR ((char *)0)
#define stricmp strcasecmp
#define strnicmp strncasecmp
#define NUMMBX	40
#define MBFWD 1
#define READ_TEXT "rt"
int FWDmode = 1;
int FWDminidle = 30;
int FWDlimittime = 0;
char *skipnonwhite (char *cp);
char *skipwhite (char *cp);
void rip (register char *s);
char *strlwr(char *s);
#else
#include "files.h"
#include "mailbox.h"
#undef connect
#endif


static const char *tokens[] = {
	"alttime", "alwaysupdate", "areas", "bulletins", "clockoffset",
	"clockset", "connect", "dumbpms", "elif", "else", "endif",
	"fbbsize", "if", "limittime", "limittype", "minidle", "personals",
	"poll",	"revincoming", "script", "size", "subchannel", "tries"
};


#define TOKEN_ALTTIME		0
#define TOKEN_ALWAYSUPD		1
#define TOKEN_AREAS		2
#define TOKEN_BULLETINS		3
#define TOKEN_CLOCKOFFSET	4
#define TOKEN_CLOCKSET		5
#define TOKEN_CONNECT		6
#define TOKEN_DUMBPMS		7
#define TOKEN_ELIF		8
#define TOKEN_ELSE		9
#define TOKEN_ENDIF		10
#define TOKEN_FBBSIZE		11
#define TOKEN_IF		12
#define TOKEN_LIMITTIME		13
#define TOKEN_LIMITTYPE		14
#define TOKEN_MINIDLE		15
#define TOKEN_PERSONALS		16
#define TOKEN_POLL		17
#define TOKEN_REVINCOMING	18
#define TOKEN_SCRIPT		19
#define TOKEN_SIZE		20
#define TOKEN_SUBCHANNEL	21
#define TOKEN_TRIES		22
#define MAXTOKENS		23


#define IFTRUE			1
#define IFFALSE			0
#define IFFOUND			2
#define IFINVALID		-1



#ifdef MBFWD
/* get any groups of four digits that specify the begin and ending hours of
 * forwarding. Returns 1 if forwarding may take place.
 */
static int
timeok(char *line)
{
time_t now;
int h1, h2, m1, m2;
struct tm *t;

	if (strlen (line) < 9)
		return 0;

	h1 = (*line++ - '0') * 10;
	h1 += (*line++ - '0');
	m1 = (*line++ - '0') * 10;
	m1 += (*line++ - '0');
	if (*line++ != '-')
		return 0;
	h2 = (*line++ - '0') * 10;
	h2 += (*line++ - '0');
	m2 = (*line++ - '0') * 10;
	m2 += (*line++ - '0');

	(void) time(&now);
	t = localtime (&now);
	if (h1 > t->tm_hour || h2 < t->tm_hour)
		return 0;
	if (h1 == t->tm_hour && m1 > t->tm_min)
		return 0;
	if (h2 == t->tm_hour && m2 < t->tm_min)
		return 0;

	return 1;	/* not too early or too late */
}



static int
dayok(char *line)
{
struct tm *t;
time_t now;
char *cp;
int startday, endday;

	cp = strchr (line, '-');
	if (cp)	{
		*cp++ = 0;
		endday = (atoi (cp) % 7);
	} else
		endday = (atoi (line) % 7);

	startday = (atoi (line) % 7);
	
	now = time((time_t *)0);
	t = localtime (&now);
	if (t->tm_wday < startday || t->tm_wday > endday)
		return 0;
	else
		return 1;
}


									
static int
gettoken (char *str)
{
int k;

	for (k = 0; k < MAXTOKENS; k++)
		if (!strnicmp (str, tokens[k], strlen(str)))
			break;
	return k;
}



struct fwdbbs *
fwdread (char *name OPTIONAL, int try OPTIONAL)
{
struct fwdbbs *f;
FILE *fp, *fpsave = NULLFILE;
char buffer[81], *cp, *tp, *next;
int thestate = OUTSIDE_BBS;
struct fscript *s, *lasts = NULLFSCRIPT;
struct arealist *a, *lasta = NULLAREALIST;
struct altlist *alt, *lastalt = NULLALTLIST;
int token, iflevel = 0;
char ifcondition[IFLEVELS + 1];
int cnt = 0;

	if ((fp = fopen (Forwardfile, "r")) == NULLFILE)
		return NULLFWDBBS;

	ifcondition[iflevel] = IFINVALID;
	f = callocw (1, sizeof (struct fwdbbs));
	if (f == NULLFWDBBS)
		return f;

	while (fgets (buffer, 80, fp))	{
		if (*buffer != '[')
			continue;
		if ((cp = strchr (buffer, ']')) == NULLCHAR)
			continue;
		*cp = 0;
		if ((!name && cnt++ == try) || (name && !stricmp (&buffer[1], name)))	{
			f->name = strdup (&buffer[1]);
			(void) strlwr (f->name);
			try = 1;
			break;
		}
	}

	if (!f->name)	{
		(void) fclose (fp);
		free (f);
		return NULLFWDBBS;
	}
	thestate = INSIDE_BBS;
	f->ALTtime = 720;
	f->subchannel = -1;
	f->minidle = FWDminidle;
	f->limittime = FWDlimittime;
	f->maxtries = 1;
	f->subchannel = (char) NOSUBCHANNEL;
	f->personals = f->bulletins = 1;
	f->limittype = FWDTYPE_FBB;

	while (1)	{			/*lint !e716 !e774 */
		if (fgets (buffer, 80, fp) == NULLCHAR)	{
			if (fpsave)	{
				(void) fclose (fp);
				fp = fpsave;
				fpsave = NULLFILE;
				continue;
			}
			break;
		}
		rip (buffer);
		cp = skipwhite (buffer);
		if (*cp == '-' || *cp == '[')
			break;
		if (!*cp || *cp == '#' || *cp == ';')
			continue;
		if (*cp == '}')	{
			thestate = INSIDE_BBS;
			continue;
		}
		if (!strnicmp (cp, "include", 7)) {
			cp = skipwhite (&cp[7]);
			fpsave = fp;
			if((fp = fopen (cp, READ_TEXT)) == NULLFILE)	{
				fp = fpsave;
				fpsave = NULLFILE;
			}
			continue;
		}

		switch (thestate)	{
			case INSIDE_BBS:
				if ((tp = strchr (cp, '=')) != NULLCHAR)	{
					*tp = 0;
					next = skipwhite (tp + 1);
					while (*(tp - 1) == ' ')
						*--tp = 0;
				} else
					next = &cp[strlen(cp)];
				if ((tp = strchr (cp, ' ')) != NULLCHAR)
					*tp++ = 0;
				token = gettoken(cp);
				if ((ifcondition[iflevel] == IFFOUND) && token != TOKEN_ENDIF)
					continue;
				if ((ifcondition[iflevel] == IFFALSE) && token != TOKEN_ELSE && token != TOKEN_ELIF && token != TOKEN_ENDIF)
					continue;
				switch (token)	{
					case TOKEN_ELIF:	if (!iflevel)
									break;
								if (ifcondition[iflevel] == IFTRUE)	{
									ifcondition[iflevel] = IFFOUND;
									break;
								}
								iflevel--;
								/* and fall through */
					case TOKEN_IF:		if (iflevel < IFLEVELS)		{
									/* set ifcondition to 1 if true, 0 if false */
									if (!strnicmp (tp, "try", 3))
										ifcondition[++iflevel] = (try == atoi (next));
									else if (!strnicmp (tp, "time", 4))
										ifcondition[++iflevel] = (char) timeok (next);
									else if (!strnicmp (tp, "mode", 4))
										ifcondition[++iflevel] = (FWDmode == atoi (next));
									else if (!strnicmp (tp, "dayofweek", 9))
										ifcondition[++iflevel] = (char) dayok (next);
									else if (token == TOKEN_ELIF)
										iflevel++;
								}
								break;
					case TOKEN_ELSE:	if (iflevel && ifcondition[iflevel] != IFFOUND)
									ifcondition[iflevel] = (ifcondition[iflevel] == IFTRUE) ? IFFOUND : IFTRUE;
								break;
					case TOKEN_ENDIF:	if (iflevel)
									ifcondition[iflevel--] = IFINVALID;
								break;
					case TOKEN_ALTTIME:	f->ALTtime = atol (next);
								break;
					case TOKEN_LIMITTIME:	f->limittime = (time_t) (atoi (next) * 60);
								break;
					case TOKEN_AREAS:	thestate = FWD_AREAS;
								break;
					case TOKEN_SCRIPT:	thestate = FWD_SCRIPT;
								break;
					case TOKEN_CLOCKSET:	f->noclockset = !stricmp (next, "no");
								break;
					case TOKEN_ALWAYSUPD:	f->alwaysupdate = !stricmp (next, "yes");
								break;
					case TOKEN_CLOCKOFFSET:	f->clockoffset = (char) atoi (next);
								break;
					case TOKEN_SIZE:	f->maxsize = atol (next);
								break;
					case TOKEN_MINIDLE:	f->minidle = atoi (next);
								break;
					case TOKEN_FBBSIZE:	f->fbbsize = atol (next);
								break;
					case TOKEN_LIMITTYPE:	if (!strnicmp (next, "FBB-NON", 7))
									f->limittype = FWDTYPE_FBBNON;
								else if (!strnicmp (next, "MBL", 3) || !strnicmp (next, "RLI", 3))
									f->limittype = FWDTYPE_MBLRLI;
								else if (!strnicmp (next, "FBB", 3))
									f->limittype = FWDTYPE_FBB;
								else if (!strnicmp (next, "X-", 2))
									f->limittype = FWDTYPE_XFWD;
								break;
					case TOKEN_TRIES:	f->maxtries = (short) atol (next);
								break;
					case TOKEN_SUBCHANNEL:	f->subchannel = (char) atoi (next);
								break;
					case TOKEN_REVINCOMING:	f->noreverseincoming = !stricmp (next, "no");
								break;
					case TOKEN_PERSONALS:	f->personals = !stricmp (next, "yes");
								break;
					case TOKEN_DUMBPMS:	f->dumbpms = !stricmp (next, "yes");
								break;
					case TOKEN_BULLETINS:	f->bulletins = !stricmp (next, "yes");
								break;
					case TOKEN_POLL:	f->poll = !stricmp (next, "yes");
								break;
					case TOKEN_CONNECT:	f->conn = strdup (next);
								break;
					default:		break;
				}
				break;
			case FWD_SCRIPT:
				s = callocw (1, sizeof(struct fscript));
				if (s != NULLFSCRIPT)	{
					if (!strnicmp(cp, "wait", 4))	{
						s->linetype = SCRIPT_WAIT;
						tp = skipnonwhite (cp);
						tp = skipwhite (tp);
						s->timeout = (short) atoi (tp);
						tp = skipnonwhite (tp);
						tp = skipwhite (tp);
						s->string = strdup (tp);
					} else if (!strnicmp (cp, "send", 4) || !strnicmp (cp, "sid", 3))	{
						tp = skipnonwhite (cp);
						tp = skipwhite (tp);
						s->string = strdup (tp);
						if (!strnicmp(cp, "sid", 3))
							s->linetype = SCRIPT_SID;
						else
							s->linetype = SCRIPT_SEND;
					} else	{
						free (s);
						break;
					}
					if (lasts)
						lasts->next = s;
					else
						f->script = s;
					lasts = s;
				}
				break;
			case FWD_AREAS:
				a = callocw (1, sizeof(struct arealist));
				if (a != NULLAREALIST)	{
					tp = skipnonwhite (cp);
					*tp++ = 0;
					a->name = strdup (cp);
					tp = skipwhite (tp);
					if (*tp && strnicmp (tp, "ALT", 3))	{
						cp = tp;
						tp = skipnonwhite (tp);
						*tp++ = 0;
						a->forceaddr = strdup (cp);
					}
					lastalt = NULLALTLIST;
					while ((tp = skipwhite (tp)) != NULLCHAR)	{
						if (strnicmp (tp, "ALT", 3))
							break;
						if (tp[3] != '=')	{
							a->isALT = 1;
							break;
						}
						alt = callocw (1, sizeof(struct altlist));
						if (alt != NULLALTLIST)	{
							alt->minutes = f->ALTtime;
							cp = &tp[4];
							tp = strpbrk (cp, ", \t");
							if (tp)	{
								next = tp;
								if (*tp++ == ',')	{
									alt->minutes = atoi(tp);
									tp = skipnonwhite(tp);
								}
								*next = 0;
							} else
								tp = &cp[strlen(cp)];
							alt->name = strdup (cp);
							if (lastalt)
								lastalt->next = alt;
							else
								a->alternates = alt;
							lastalt = alt;
						}
					}
					if (lasta)
						lasta->next = a;
					else
						f->areas = a;
					lasta = a;
				}
				break;
			default:
				break;
		}
	}

	(void) fclose (fp);
	return f;
}

#else



struct fwdbbs *
fwdread (char *name OPTIONAL, int try OPTIONAL)
{
	return NULLFWDBBS;
}
#endif



void
fwdfree (struct fwdbbs **f)
{
struct fscript *s, *stmp;
struct arealist *a, *atmp;
struct altlist *alt, *alttmp;

	if (*f == NULLFWDBBS)
		return;
	free ((*f)->name);
	free ((*f)->conn);
	s = (*f)->script;
	while (s)	{
		free (s->string);
		stmp = s;
		s = s->next;
		free (stmp);
	}
	a = (*f)->areas;
	while (a)	{
		free (a->name);
		free (a->forceaddr);
		alt = a->alternates;
		while (alt)	{
			free (alt->name);
			alttmp = alt;
			alt = alt->next;
			free (alttmp);
		}
		atmp = a;
		a = a->next;
		free (atmp);
	}
	free (*f);
	*f = NULLFWDBBS;
}


#ifdef TEST
char *
strlwr(s)
char *s;
{
register char *p = s;

	while (*p)
		*p = (char) tolower (*p), p++;
	return s;
}


char *
skipnonwhite (cp)
char *cp;
{
        while (*cp && *cp != ' ' && *cp != '\t')
                cp++;
        return (cp);
}


char *
skipwhite (cp)
char *cp;
{
        while (*cp && (*cp == ' ' || *cp == '\t'))
                cp++;
        return (cp);
}



void
rip(s)
register char *s;
{
        register char *cp;

        if((cp = strchr(s,'\n')) != NULLCHAR)
                *cp = '\0';
}


int
main (int argc, char *argv[])
{
struct fwdbbs *f;
struct fscript *s;
struct arealist *a;
struct altlist *alt;
int try = 1;

	if (argc < 2)	{
		printf ("No BBS name given\n");
		exit (1);
	}
	if (argc > 2)	{
		try = atoi (argv[2]);
		if (!try)
			try = 1;
	}
	f = fwdread (argv[1], try);
	if (f == NULLFWDBBS)	{
		printf ("BBS '%s' not found\n", argv[1]);
		exit (1);
	}
	printf ("[%s]\nCONNECT = %s\nALTTIME = %ld\nSIZE = %ld\nPOLL = %s\n",
		f->name, f->conn, f->ALTtime, f->maxsize, (f->poll) ? "YES" : "NO");
	printf ("SUBCHANNEL = %d\nREVINCOMING = %s\nFBBSIZE = %ld\n",
		f->subchannel, (f->noreverseincoming) ? "NO" : "YES",
		f->fbbsize);
	printf ("PERSONALS = %s\nBULLETINS = %s\n", (f->personals) ? "YES" : "NO",
		(f->bulletins) ? "YES" : "NO");
	printf ("CLOCKSET = %s\nCLOCKOFFSET = %d\nTRIES = %d\n",
		(f->noclockset) ? "NO" : "YES", f->clockoffset, f->maxtries);
	s = f->script;
	if (s)	{
		printf ("\nSCRIPT = {\n");
		while (s)	{
			switch (s->linetype)	{
				case SCRIPT_SEND:	printf (" SEND %s\n", s->string);
							break;
				case SCRIPT_WAIT:	printf (" WAIT %d %s\n", s->timeout, s->string);
							break;
				case SCRIPT_SID:	printf (" SID %s\n", s->string);
			}
			s = s->next;
		}
		printf ("}\n\n");
	}
	a = f->areas;
	if (a)	{
		printf ("\nAREAS = {\n");
		while (a)	{
			printf (" %s", a->name);
			if (a->forceaddr)
				printf (" %s", a->forceaddr);
			if (a->isALT)
				printf (" ALT\n");
			else {
				alt = a->alternates;
				if (alt)	{
					while (alt)	{
						printf (" ALT=%s", alt->name);
						if (alt->minutes != f->ALTtime)
							printf (",%ld", alt->minutes);
						alt = alt->next;
					}
				}
				putchar ('\n');
			}
			a = a->next;
		}
		printf ("}\n\n");
	}
	fwdfree (&f);
	return 0;
}

#endif
