/*
** Copyright 1992 Patrick Sweeney
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include "script.h"
#include "token.h"
#include "ll2.h"

static char *
salloc(char *s)
{
	char *p;

	if ((p = (char *) malloc(strlen(s) + 1)) == NULL)
		return NULL;
	strcpy(p, s);
	return p;
}

static int
script_parse_script(Tokenfile *tf, System *system)
{
	Scriptentry *entry;
	Token token;
	
	if ((system->entries = (Scriptentry *)
	     malloc(SCRIPT_ENTRIES * sizeof(Scriptentry))) == NULL)
		return token_error(tf, "out of memory");
	system->nentries = 0;
	system->maxentries = SCRIPT_ENTRIES;
	while(1)
	{
		if (token_read(tf, &token) != 0)
			return -1;
		if (token.type == TOKEN_EOF)
			return token_error(tf, "unexpected end of file!\n");
		if (token.type == TOKEN_END)
			break;
		if (system->nentries == system->maxentries)
		{
			Scriptentry *e;

			system->maxentries += SCRIPT_ENTRIES;
			if ((e = (Scriptentry *) malloc(system->maxentries *
			    sizeof(Scriptentry))) == NULL)
				return token_error(tf, "out of memory");
			memcpy((char *) e, (char *) system->entries,
			       sizeof(Scriptentry) * system->nentries);
			free((char *) system->entries);
			system->entries = e;
		}
		entry = &system->entries[system->nentries++];
		if (token.type == TOKEN_REMOTE)
		{
			entry->type = ENTRY_REMOTE;
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_WAIT &&
			    token.type != TOKEN_NOWAIT)
				return token_error(tf,"expect wait or nowait");
			entry->v.exec.wait = (token.type==TOKEN_WAIT) ? 1 : 0;
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expected string");
			if ((entry->v.exec.cmd = salloc(token.v.sval)) == NULL)
				return token_error(tf,"out of memory");
			continue;
		}
		if (token.type == TOKEN_LOCAL)
		{
			entry->type = ENTRY_LOCAL;
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_WAIT &&
			    token.type != TOKEN_NOWAIT)
				return token_error(tf,"expect wait or nowait");
			entry->v.exec.wait = (token.type==TOKEN_WAIT) ? 1 : 0;
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expected string");
			if ((entry->v.exec.cmd = salloc(token.v.sval)) == NULL)
				return token_error(tf,"out of memory");
			continue;
		}
		entry->type = ENTRY_EXPECT;
		if (token.type != TOKEN_LITERAL)
			return token_error(tf, "expected receive string!\n");
		if ((entry->v.expect.recv = salloc(token.v.sval)) == NULL)
			return token_error(tf, "out of memory!\n");
		if (token_read(tf, &token) != 0)
			return -1;
		if (token.type != TOKEN_LITERAL)
			return token_error(tf, "expected send string!\n");
		if ((entry->v.expect.send = salloc(token.v.sval)) == NULL)
			return token_error(tf, "out of memory!\n");
		if (token_read(tf, &token) != 0)
			return -1;
		if (token.type != TOKEN_LITERAL)
			return token_error(tf,"expected error send string!\n");
		if ((entry->v.expect.err_send = salloc(token.v.sval)) == NULL)
			return token_error(tf, "out of memory!\n");
	}
	return 0;
}

static int
script_parse_system(Tokenfile *tf)
{
	Token token;
	System *system;

	if (token_read(tf, &token) != 0)
		return -1;
	if (token.type != TOKEN_LITERAL)
		return token_error(tf, "expected system name");
	if ((system = (System *) malloc(sizeof(System))) == NULL)
		return token_error(tf, "out of memory");
	strcpy((char *) system->system_name, (char *) token.v.sval);
	while(1)
	{
		if (token_read(tf, &token) != 0)
			return -1;
		if (token.type == TOKEN_END)
			break;
		switch(token.type)
		{
		case TOKEN_BAUDRATE:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_INTEGER)
				return token_error(tf, "expected baud rate");
			system->baudrate = token.v.ival;
			break;
		case TOKEN_DIALER:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expected device name");
			strcpy((char *) system->dialer,
			       (char *) token.v.sval);
			break;
		case TOKEN_PHONE1:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expected phone1");
			strcpy((char *) system->phone1, (char *) token.v.sval);
			break;
		case TOKEN_PHONE2:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expected phone2");
			strcpy((char *) system->phone2, (char *) token.v.sval);
			break;
		case TOKEN_TIMEOUT:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_INTEGER)
				return token_error(tf,
				   "expected timeout value");
			system->timeout = token.v.ival;
			break;
		case TOKEN_RETRIES:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_INTEGER)
				return token_error(tf,
				   "expected retries value");
			system->retries = token.v.ival;
			break;
		case TOKEN_SCRIPT:
			if (script_parse_script(tf, system) != 0)
				return -1;
			break;
		}
	}
	if (ll2_append(tf->syslist, (char *) system) != 0)
		return -1;
	return 0;
}

static FILE *
script_open(char *pfn)
{
	FILE *f;
	struct passwd *pwd;
	char fn[FILENAME_MAX];
	char *tfn;

	/* first check dot */
	if (pfn == NULL)
	{
		tfn = fn;
		strcpy(fn, ".");
		strcat(fn, SCRIPT_NAME);
		if (access(fn, F_OK) != 0)
		{
			/* dot failed, try home directory */
			if ((pwd = getpwuid(geteuid())) == NULL)
			{
				fprintf(stderr, "can't read password information!\n");
				return NULL;
			}
			strcpy(fn, pwd->pw_dir);
			strcat(fn, SCRIPT_NAME);
			if (access(fn, F_OK) != 0)
			{
				/* home directory failed, try /usr/local/script */
				strcpy(fn, "/usr/local/script");
				strcat(fn, SCRIPT_NAME);
				if (access(fn, F_OK) != 0)
				{
					fprintf(stderr, "can't find script file!\n");
					return NULL;
				}
			}
		}
	}
	else
		tfn = pfn;
	if (access(tfn, R_OK) != 0)
	{
		fprintf(stderr, "can't open scipt file: %s... for reading!\n",
			tfn);
		return NULL;
	}
	if ((f = fopen(tfn, "r")) == NULL)
	{
		fprintf(stderr, "can't open scipt file: %s... for reading!\n",
			tfn);
		return NULL;
	}
	return f;
}

void
script_syslist_destroy(char *data)
{
	if (data)
	{
		free(data);
	}
	return;
}

static int
script_parse_modem(Tokenfile *tf)
{
	Modem *modem;
	Token token;

	if ((modem = (Modem *) malloc(sizeof(Modem))) == NULL)
		return -1;
	if (ll2_append(tf->modems, (char *) modem) != 0)
		return -1;
	if (token_read(tf, &token) != 0)
		return -1;
	strcpy((char *) modem->name, token.v.sval);
	while(1)
	{
		if (token_read(tf, &token) != 0)
			return -1;
		if (token.type == TOKEN_END)
			break;
		switch(token.type)
		{
		case TOKEN_DIALSTR:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expect string");
			strcpy(modem->dialstr, token.v.sval);
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expect string");
			strcpy(modem->dialstr_r, token.v.sval);
			break;
		case TOKEN_RESETSTR:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expect string");
			strcpy(modem->resetstr, token.v.sval);
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expect string");
			strcpy(modem->resetstr_r, token.v.sval);
			break;
		case TOKEN_HANGUPSTR:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expect string");
			strcpy(modem->hangupstr, token.v.sval);
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expect string");
			strcpy(modem->hangupstr_r, token.v.sval);
			break;
		case TOKEN_STOPBITS:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_INTEGER)
				return token_error(tf, "expected integer");
			if (token.v.ival != 1 && token.v.ival != 2)
				return token_error(tf,
				   "stop bits must be 1 or 2");
			modem->stopbits = token.v.ival;
			break;
		case TOKEN_DATABITS:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_INTEGER)
				return token_error(tf, "expected integer");
			if (token.v.ival != 5 && token.v.ival != 6 &&
			    token.v.ival != 7 && token.v.ival != 8)
				return token_error(tf,
				   "data bits must be 5, 6, 7 or 8");
			modem->databits = token.v.ival;
			break;
		case TOKEN_DEVICE:
			if (token_read(tf, &token) != 0)
				return -1;
			if (token.type != TOKEN_LITERAL)
				return token_error(tf, "expected device name");
			strcpy(modem->device, token.v.sval);
			break;
		default:
			return token_error(tf, "undefined token");
		}
	}
	return 0;
}

int
script_parse(char *fn, Systeminfo *info)
{
	Tokenfile tf;
	Token token;

	if ((info->syslist = ll2_create()) == NULL)
	{
		fprintf(stderr,"can't create llist!\n");
		return -1;
	}
	if ((info->modems = ll2_create()) == NULL)
	{
		fprintf(stderr,"can't create llist!\n");
		return -1;
	}
	if ((tf.f = script_open(fn)) == NULL)
	{
		ll2_destroy(info->syslist, NULL);
		return -1;
	}
	tf.fn = fn;
	tf.syslist = info->syslist;
	tf.modems = info->modems;
	while(!feof(tf.f))
	{
		token_read(&tf, &token);
		if (token.type == TOKEN_EOF)
			break;
		switch(token.type)
		{
		case TOKEN_SYSTEM:
			if (script_parse_system(&tf) != 0)
			{
				ll2_destroy(info->syslist,
					    script_syslist_destroy);
				return -1;
			}
			break;
		case TOKEN_MODEM:
			if (script_parse_modem(&tf) != 0)
			{
				ll2_destroy(info->syslist,
					    script_syslist_destroy);
				return -1;
			}
			break;
		default:
			(void) token_error(&tf, "unknown token!\n");
			ll2_destroy(info->syslist, script_syslist_destroy);
			return -1;
		}
	}
	return 0;
}
