/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:syscall.c 12.1$ */
/* $ACIS:syscall.c 12.1$ */
/* $Source: /ibm/acis/usr/src/etc/syscall/RCS/syscall.c,v $ */

#ifndef lint
static char *rcsid = "$Header:syscall.c 12.1$";
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>

#include <ctype.h>

/*
 * syscall
 *
 * system call interface program - used mainly to reduce the size 
 * of a miniroot by replacing commonly used utilities with one 
 * very small one.
 * format is generally:
 *	syscall name arg1 ... argn 
 * where "name" is the name of a system call (and a few additional 
 *	special cases).
 * arg1 ... argn are the arguments to the system call
 * generally of the format:
 *	string		anything not given below is passed as a string
 *			this is usually a pathname but sometimes is text
 *			(e.g. for 'write')
 *	0xdigits	hex constant
 *	0digits		octal constant
 *	digits		decimal constant
 *	&&n		address of argument n (0=system call name)
 *	&n		address of byte n of an internal 10k buffer
 *	#text		length of given text
 *	$n		result of previous syscall (n=0 is first)
 *
 * e.g. to simulate x=open("x",1); write(x,"hello",strlen("hello"))
 * one could do:
 *	syscall open x 1 \; write \$0 hello \#hello
 */

/* 
 * include system call name table 
 */

#include <sys/../sys/syscalls.c>

#ifdef DEBUG
#define DEBUGF(cond,stmt) if (cond) stmt	/* for debugging printf's */
#else
#define DEBUGF(cond,stmt) 		/* do nothing if not debugging */
#endif

#define MAXARG 6
#define EXTRA_ARGS	1024	/* extra for execl */
#define MAXRESULTS	128	/* number of syscall results to remember */
char *args[MAXARG+EXTRA_ARGS];	/* args to each system call */
char *fmts[MAXARG+EXTRA_ARGS];	/* format strings for debugging printf's */
#define NSYSCALLS (sizeof syscallnames) / sizeof syscallnames[0]
#ifdef PARSE
int cflg;			/* -c used? */
#endif PARSE
int sflg;			/* -s used? */
int vflg;			/* -v used? */
char buff[10240];		/* buffer for misc stuff */
int results[MAXRESULTS];	/* results of system calls */
int count;			/* # of this system call on command line */
int repeat = 1;			/* # of repetitions */
int escape;			/* character that escapes non-strings */
char *progname;			/* program name */

badsys()
{
	err("bad system call");
}

main(argc,argv)
	char **argv;
{

	register int i;
	register char *argp;


	for (i=1; i<MAXARG; ++i)
		args[i] = 0;
	--argc;
	++argv;
	while (argc > 0 && *(argp = argv[0]) == '-')
		{
		++argv;
		--argc;
		switch(argp[1])
			{
		case 'v':		/* verbose */
			++vflg;
			break;
		case 'p':
			progname = argp+2;
			break;
		case 's':		/* silent */
			++sflg;
			break;
#ifdef PARSE
		case 'c':
			++cflg;		/* parse the arguments */
			break;
#endif PARSE
		case 'e':
			escape = argp[2];
			break;
		default:
			if (isdigit(argp[1]))
				{
				repeat = atoi(argp+1);
				break;
				}
			}
		}
	signal(SIGSYS, badsys);
	do
		{
#ifdef PARSE
		if (cflg)
			parseit(argc,argv);
		else
#endif PARSE
			doonce(argc,argv);
		}
	while (--repeat > 0);
	exit(0);
}

#ifdef PARSE
/*
 * parse each argument in turn and execute it
 */
parseit(argc,argv)
char **argv;
{
	char newargv[MAXARG+EXTRA_ARGS];
	char strings[10240];
	int newargc;
	int i;

	for (i=0; i<argc; ++i)
		{
		DEBUGF (vflg, printf("parse(%s)\n",argv[i]));
		newargc = _parse(argv[i], strings, newargv);
		DEBUGF (vflg, printf("parse found %d arguments\n",newargc));
		doonce(newargc,newargv);
		}
}
#endif PARSE

doonce(argc,argv) 
	register int argc;
	register char **argv;
{
	register int nargs;
	count = 0;
	for (; argc > 0; argc -= nargs, argv += nargs)
		nargs = docall(argc,argv,args);
}

docall(argc,argv,args)
	register char **argv;
	register char **args;
{
	register char *cmd;
	register int nargs;
	register int i;
	register char *argp;
	register int n;
	extern int errno;

	cmd=argv[0];
	DEBUGF (vflg, printf("%s ",cmd));
	for (i=1; i<argc; ++i)
		{
		register char *fmt = "0x%x";
		argp = argv[i];
		if (strcmp(argp,";") == 0)
			break;
		if (escape && argp[0] != escape)
			args[i] = argp, fmt = "%s";
		else {
			if (escape)
				++argp;
			if (argp[0] == '0' && (argp[1] == 'x' || argp[1] == 'X'))
				args[i] = (char *) atox(argp+2);
			else if (argp[0] == '0' && isdigit(argp[1]))
				args[i] = (char *) atoo(argp);
			else if (isdigit(*argp) || ((*argp == '+' || *argp == '-') && isdigit(argp[1])))
				args[i] = (char *) atoi(argp);
			else if (argp[0] == '"' || argp[0] == '\'' || argp[0] == '\\')
				args[i] = argp+1, fmt = "%s";
			else if (argp[0] == '&')
				{
				if (argp[1] == '&')
					args[i] = (char *) (args+atoi(argp+2));
				else if (argp[1] == '$')
					{
					args[i] = (char *) &results[atoi(argp+2)];
/*					printf("args[i]=%x results=%x atoi(argp+2)=%x\n",args[i], results, atoi(argp+2));	/* DEBUG */
					}
				else
					args[i] = buff + atoi(argp+1);	/* use pre-allocated buffer */
				}
			else if (argp[0] == '$')
				{
				args[i] = (char *) results[argp[1] ? 
					((n = atoi(argp+1)) >= 0 ? n : count+n)
					: (count-1)];
				}
			else if (argp[0] == '#')
				args[i] = (char *) strlen(argp+1);
			else
				args[i] = argp, fmt = "%s";
				}
		DEBUGF(vflg,printf(" arg %d = ", i));
		DEBUGF(vflg,printf(fmt, args[i]));
		fmts[i] = fmt;
		}
	DEBUGF(vflg, printf("\n"));
	nargs = i+1;
	errno = 0;
	
	if (strcmp(cmd,"if") == 0)
		{
		if (i=ifcmd(args[1], args[2], args[3]))
			{
			args += 4;
			cmd = args[0];
			DEBUGF(vflg, printf("if true\n"));
			}
		else
			{
			DEBUGF(vflg, printf("if false\n"));
			cmd = 0;
			}
		}
	if (cmd == 0)
		;		/* we failed the if above */
	else if (strcmp(cmd,"=") == 0)
		{
		i=ifcmd(args[1], args[2], args[3]);
		if (nargs > 5)
			{
			DEBUGF(vflg, printf("store %d in %d\n",i,args[4]));
			results[(int) args[4]] = i;
			}
		}
	else
#if defined(CTIME)
	if (strcmp(cmd,"ctime") == 0)
		i = (int) ctime(args[1]);
	else
	if (strcmp(cmd,"gtime") == 0)
		i = (int) gtime(args[1],args[2]);
	else
#endif CTIME
#if defined(PRINTF) || defined(DEBUG)
	if (strcmp(cmd,"printf") == 0)
		{
		printf(args[1],args[2],args[3],args[4],args[5]);
		return(nargs);
		}
	else
#endif PRINTF
	if (strcmp(cmd,"sleep") == 0)
		i = sleep(args[1]);
	else if (strcmp(cmd,"sbrk") == 0)
		i = sbrk(args[1]);
	else if (strcmp(cmd,"strlen") == 0)
		i = strlen(args[1]);
	else if (strcmp(cmd,"execl") == 0)
		i = execv(args[1],&args[2]);	/* looks odd but its right */
	else
		{
		i = getsyscall(cmd);
		if (i < 0 || i>=NSYSCALLS)
			error(cmd,"not a system call");
		
		args[0] = (char *) i;		/* system call number */
		i = syscall(args[0],args[1],args[2],args[3],args[4],args[5]);
		}

	if (count >= MAXRESULTS)
		error("too many commands",(char *) 0);
	results[count] = i;
	if (i == -1 && errno)
		{
		extern int sys_nerr;
		extern char *sys_errlist[];

		error(cmd, errno < sys_nerr ? sys_errlist[errno] : "unknown");
		}
	else DEBUGF(vflg, printf("result%d=%d\n",count,i));
	++count;
	return(nargs);
}

/*
 * convert string in hex to binary
 */
int atox(ptr)
	register char *ptr;
{
	register int n = 0;
	register int c;

	DEBUGF(vflg > 1, printf("atox(%s)",ptr));
	for (; c = *ptr++;) {
		if (isdigit(c))
			c -= '0';
		else if ('a' <= c && c <= 'f')
			c -= 'a' - 10;
		else if ('A' <= c && c <= 'F')
			c -= 'A' - 10;
		else
			break;
		n = (n << 4) + c;
	}
	DEBUGF(vflg > 1, printf("=%x\n",n));
	return (n);
}


/*
 * convert string in octal to binary
 */
int atoo(ptr)
	register char *ptr;
{
	register int n = 0;
	register int c;

	DEBUGF(vflg > 1, printf("atoo(%s)",ptr));
	for (; c = *ptr++;) {
		if (('0' <= c) && (c <= '7'))
			c -= '0';
		else
			break;
		n = (n << 3) + c;
	}
	DEBUGF(vflg > 1, printf("=%o\n",n));
	return (n);
}

err(s)
char *s;
{
	perror(s);
	exit(1);
}

getsyscall(cmd) 
	register char *cmd;
{
	register int i;

	if (isdigit(cmd[0]))
		return(atoi(cmd));
	for (i=0; i<NSYSCALLS; ++i)
		if (strcmp(cmd,syscallnames[i]) == 0)
			return(i);
	return(-1);
}

ifcmd(lhs,op,rhs)
int lhs, rhs;
char *op;
{
int result;

#define OP(o) if (strcmp(op,"o") == 0) return(lhs o rhs)

	OP(>);
	OP(<);
	OP(==);
	OP(!=);
	OP(<=);
	OP(>=);
	OP(<<);
	OP(>>);
	OP(&);
	OP(^);
	OP(+);
	OP(-);
	OP(*);
	OP(/);
	OP(<<);
	OP(>>);

	if (strcmp(op,"!") == 0)
		return(fetch(lhs,rhs));

	error("bad operator",op);
}

#ifdef NO_STDIO
/* get rid of stdio exit if we're using our own (standalone) printf */
exit(n)
{
_exit(n);
}
#endif

error(msg,value)
char *msg, *value;
{
	if (progname) {
		write(2,progname, strlen(progname));
		write(2, " ", 1);
	}
	write(2,msg,strlen(msg));
	if (value) {
		write(2, ": ", 2);
		write(2,value,strlen(value));
	}
	write(2,"\n",1);
	exit(1);
}

fetch(addr,size)
char *addr;
{
	if (size == sizeof (long))
		return(* (long *) addr);
	if (size == sizeof (short))
		return(* (unsigned short *) addr);
	if (size == sizeof (char))
		return(* (unsigned char *) addr);
	error("RHS of ! isn't 1, 2, or 4",(char *) 0);
}

#ifdef CTIME
static char	*ep, *sp;

static	int	dmsize[12] =
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

gtime(ap,uflag)
char *ap;
{
	register int i, year, month;
	int day, hour, mins, secs;
	struct tm *L;
	char x;
	struct	timeval tv;
	struct	timezone tz;

	ep = ap;
	while(*ep) ep++;
	sp = ap;
	while(sp < ep) {
		x = *sp;
		*sp++ = *--ep;
		*ep = x;
	}
	sp = ap;
	gettimeofday(&tv, &tz);
	L = localtime(&tv.tv_sec);
	secs = gp(-1);
	if (*sp != '.') {
		mins = secs;
		secs = 0;
	} else {
		sp++;
		mins = gp(-1);
	}
	hour = gp(-1);
	day = gp(L->tm_mday);
	month = gp(L->tm_mon+1);
	year = gp(L->tm_year);
	if (*sp)
		return (0);
	if (month < 1 || month > 12 ||
	    day < 1 || day > 31 ||
	    mins < 0 || mins > 59 ||
	    secs < 0 || secs > 59)
		return (0);
	if (hour == 24) {
		hour = 0;
		day++;
	}
	if (hour < 0 || hour > 23)
		return (0);
	tv.tv_sec = 0;
	year += 1900;
	for (i = 1970; i < year; i++)
		tv.tv_sec += dysize(i);
	/* Leap year */
	if (dysize(year) == 366 && month >= 3)
		tv.tv_sec++;
	while (--month)
		tv.tv_sec += dmsize[month-1];
	tv.tv_sec += day-1;
	tv.tv_sec = 24*tv.tv_sec + hour;
	tv.tv_sec = 60*tv.tv_sec + mins;
	tv.tv_sec = 60*tv.tv_sec + secs;
	/* convert to GMT assuming local time */
	if (uflag == 0) {
		tv.tv_sec += (long)tz.tz_minuteswest*60;
		/* now fix up local daylight time */
		if (localtime(&tv.tv_sec)->tm_isdst)
			tv.tv_sec -= 60*60;
	}
	return (tv.tv_sec);
}

gp(dfault)
{
	register int c, d;

	if (*sp == 0)
		return (dfault);
	c = (*sp++) - '0';
	d = (*sp ? (*sp++) - '0' : 0);
	if (c < 0 || c > 9 || d < 0 || d > 9)
		return (-1);
	return (c+10*d);
}
#endif CTIME
