/*
** 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"

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;
    char pwbuf[64];		/* monstrous password */

    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;
	switch (token.type) {
	case TOKEN_LITERAL:
	    if ((entry->v.expect.send = salloc(token.v.sval)) == NULL)
		return token_error(tf, "out of memory!\n");
	    break;
	case TOKEN_PASSWORD:
	    fprintf(stderr, "Enter the password for %s: ", system->system_name);
	    gets_noecho(pwbuf, sizeof pwbuf);
	    fprintf(stderr, "\n");
	    if ((entry->v.expect.send = salloc(pwbuf)) == NULL)
		return token_error(tf, "out of memory!\n");
	    break;
	default:
	    return token_error(tf, "expected send string!\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 *tfn)
{
    FILE *f;
    struct passwd *pwd;
    char fn[FILENAME_MAX];

    /* search path: ./SCRIPT_NAME, ~/.term/SCRIPT_NAME */
    if (tfn == NULL) {
	tfn = fn;
	strcpy(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, "/.term/");
	    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;
		}
	    }
	}
    }
    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;
}
