// Copyright (C) 1999-2001 Open Source Telecom Corporation.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception to the GNU General Public License, permission is
// granted for additional uses of the text contained in its release
// of ccscript.
//
// The exception is that, if you link the ccscript library with other
// files to produce an executable, this does not by itself cause the
// resulting executable to be covered by the GNU General Public License.
// Your use of that executable is in no way restricted on account of
// linking the ccscript library code into it.
//
// This exception does not however invalidate any other reasons why
// the executable file might be covered by the GNU General Public License.
//
// This exception applies only to the code released under the
// name ccscript.  If you copy code from other releases into a copy of
// ccscript, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for ccscript, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.

#include <cc++/config.h>
#include <cc++/misc.h>
#include <cc++/slog.h>
#include <cc++/process.h>
#include <cc++/strchar.h>
#include <cstdlib>
#ifdef	WIN32
#include <process.h>
#else
#include <sys/wait.h>
#endif
#include <iostream>
#include <fstream>
#include "script3.h"

#ifndef	WEXITSTATUS
#define	WEXITSTATUS(x)	(x)
#endif

#ifdef	CCXX_NAMESPACES
using namespace ost;
using namespace std;
#endif

static ofstream output;
static char *sigtype[2] = {NULL, NULL};
static bool exited = false;
static int oddeven = 0;

class shScript : public ScriptRuntime
{
public:
	shScript();
} _image;

class shMethods : public ScriptMethods
{
public:
	bool scrEcho(void);
	bool scrSystem(void);
	bool scrAlarm(void);
	bool scrToken(void);
	bool scrMask(void);
};

enum
{
	SIGEXIT = 0
};

class shInterp : public ScriptInterp, public Semaphore
{
private:
	friend class shScript;

	void exitThread(const char *errmsg);

	bool exit(void)
	{
		if(!exiting)
			if(ScriptInterp::exit())
				return true;

		cerr << "exiting..." << endl; detach(); ::exit(0);
	};

public:
	void waitThread(void);

	void signal(const char *sigid)
		{ScriptInterp::signal(sigid);};
	shInterp();
} interp;

class shCompiler : public ScriptCompiler
{
public:
	Name *getScript(const char *name);

	shCompiler();
} shCompiler;

shCompiler::shCompiler() :
ScriptCompiler(&_image, "/ccscript/define")
{
}

Script::Name *shCompiler::getScript(const char *name)
{
	static bool started = false;
	char fname[128], lname[128];
	const char *ext;
	Name *line = ScriptImage::getScript(name);
	if(!line)
	{
		strcpy(fname, name);
		ext = strrchr(name, '.');
		if(!ext)
			strcat(fname, ".scr");
		if(!canAccess(fname) && !started)
		{
			cerr << "testscript: " << fname << ": cannot execute" << endl;
			::exit(-1);
		}
		compile(fname);
		if(ext && stricmp(ext, ".scr"))
		{
			snprintf(lname, sizeof(lname), "exec::%s", name);
			name = lname;
		}
		line = ScriptImage::getScript(name);
		if(!line && !started)
		{
			cerr << "testscript: " << fname << ": compile failed" << endl;
			::exit(-1);
		}
		if(started)
			return line;
		strcpy(fname, name);
		strcat(fname, ".out");
		output.open(fname, ios::trunc);
		started = true;
		commit();
	}
	return line;
}

shInterp::shInterp() :
ScriptInterp(), Semaphore()
{
}

bool shMethods::scrMask(void)
{
	Line *line = getLine();
	Name *scr = getName();
	char buf[128];

	snprintf(buf, sizeof(buf), "mask: %08lx, line: %08lx",
		scr->mask, line->mask);
	cout << buf << endl;
	advance();
	return true;
}

bool shMethods::scrEcho(void)
{
	const char *val;
	while(NULL != (val = ScriptInterp::getValue(NULL)))
	{
		output << val;
		cout << val;
	}

	output << endl;
	cout << endl;
	advance();
	return true;
}

void shInterp::exitThread(const char *errmsg)
{
	ScriptInterp::exitThread(errmsg);
	post();
}

void shInterp::waitThread(void)
{
	timeout_t timer = getTimeout();

	if(!timer)
		return;

	if(!wait(timer))
	{
		enter();
		if(!updated)
		{
			if(!ScriptInterp::signal("timeout"))
				error("timeout-expired");
			updated = true;
		}
		leave();
	}
	delete thread;
	thread = NULL;
}

bool shMethods::scrAlarm(void)
{
#ifndef	WIN32
	alarm(atoi(ScriptInterp::getValue("0")));
#endif
	advance();
	return true;
}

bool shMethods::scrToken(void)
{
	unsigned argc = 0;
	Line *line = getLine();

	while(argc < line->argc)
		cout << "[-" << line->args[argc++] << "-] ";

	cout << endl;
	advance();
	return true;
}

bool shMethods::scrSystem(void)
{
	const char *argv[33];
	int argc = 0;
	const char *val;
	int status;
#ifndef	WIN32
	int pid;
#endif

	while(argc < 32 && (NULL != (val = ScriptInterp::getValue(NULL))))
		argv[argc++] = val;

	argv[argc] = NULL;

#ifdef	WIN32
	status = (int)_spawnvp(_P_WAIT, argv[0], argv);
#else
	pid = vfork();
	if(!pid)
	{
		execvp(*argv, (char **)argv);
		::exit(-1);
	}
#ifdef	__FreeBSD__
	wait4(pid, &status, 0, NULL);
#else
	waitpid(pid, &status, 0);
#endif
	status = WEXITSTATUS(status);
#endif
	if(status)
		error("sys-error");
	else
		advance();
	return false;
}

shScript::shScript() :
ScriptRuntime()
{
	static Script::Define interp[] = {
		{"echo", false, (Method)&shMethods::scrEcho,
			(Check)&ScriptChecks::chkHasArgs},
		{"sys", false, (Method)&shMethods::scrSystem,
			(Check)&ScriptChecks::chkHasArgs},
		{"alarm", false, (Method)&shMethods::scrAlarm,
			(Check)&ScriptChecks::chkHasArgs},
		{"token", true, (Method)&shMethods::scrToken,
			(Check)&ScriptChecks::chkIgnore},
		{"mask", true, (Method)&shMethods::scrMask,
			(Check)&ScriptChecks::chkIgnore},
		{NULL, false, NULL, NULL}};

	trap("alarm");
	trap("hangup");
	trap("timeout");

	load(interp);

	Script::symsize = 64;
	Script::pagesize = 1024;
	Script::symlimit = (unsigned)Script::pagesize - 64;
}

#ifndef	WIN32
static RETSIGTYPE handler(int signo)
{
	switch(signo)
	{
	case SIGINT:
		if(exited)
			return;
		alarm(0);
		sigtype[oddeven] = "exit";
		break;
	case SIGHUP:
		if(sigtype[oddeven] && stricmp(sigtype[oddeven], "alarm"))
			return;
		alarm(0);
		sigtype[oddeven] = "hangup";
		break;
	case SIGALRM:
		if(!sigtype[oddeven])
			sigtype[oddeven] = "alarm";
		alarm(0);
		break;
	}
}
#endif

int main(int argc, char **argv)
{
	char *ext;
	int stepper;
	char argname[10];
	char lname[128];
	int argcount = 0;
	char *name;

	Script::use_merge = true;

	if(argc < 2)
		::exit(-1);

	if(argc != 2)
	{
		cerr << "use: testscript scriptname" << endl;
		::exit(-1);
	}

#ifdef	WIN32
	Script::plugins = "../w32/debug";
#else
	if(isDir("../modules/.libs"))
		Script::plugins = "../modules/.libs";
	else
		Script::plugins = "../modules";
#endif

	ext = strrchr(argv[1], '.');
	if(ext)
		if(!stricmp(ext, ".scr"))
			*ext = 0;

	slog("testscript", Slog::classUser, Slog::levelInfo);

	shCompiler.compileDefinitions("script.def");

	_image.aliasModule("str", "string");

	name = argv[1];
	shCompiler.getScript(name);

	if(ext && *ext)
	{
		snprintf(lname, sizeof(lname), "exec::%s", name);
		name = lname;
	}

	if(!interp.attach(&_image, name))
		::exit(-1);

	while(argcount < argc)
	{
		snprintf(argname, sizeof(argname), "argv.%d", argcount);
		interp.setConst(argname, argv[argcount++]);
	}
	snprintf(argname, sizeof(argname), "%d", argcount);
	interp.setConst("argv.count", argname);

#ifndef	WIN32
	Process::setPosixSignal(SIGINT, &handler);
	Process::setPosixSignal(SIGHUP, &handler);
	Process::setPosixSignal(SIGALRM, &handler);
#endif

	while(!interp.done())
	{
		stepper = oddeven;
		if(oddeven)
			oddeven = 0;
		else
			++oddeven;
		if(sigtype[stepper])
		{
			interp.signal(sigtype[stepper]);
			sigtype[stepper] = NULL;
		}
		interp.step();
		interp.waitThread();
	}

	interp.detach();
	::exit(-1);
	return -1;
}



syntax highlighted by Code2HTML, v. 0.9.1