/* shell.c */

/* Author:
 *	Guntram Blohm
 *	Buchenstrasse 19
 *	7904 Erbach, West Germany
 *	Tel. ++49-7305-6997
 *	sorry - no regular network connection
 */

/*
 * This file contains a minimal version of a shell for TOS. It allows the
 * setting of an environment, calling programs, and exiting.
 * If you don't have another one, this might be sufficient, but you should 
 * prefer *any* other shell.
 * You may, however, want to set your SHELL environment variable to this
 * shell: it implements the -c switch, which is required by Elvis, and
 * not supported by most other atari shells.
 */

#include <stdio.h>
#include <string.h>
#include <osbind.h>
#ifdef __STDC__
#include <stdlib.h>
#else
extern char *getenv(), *malloc();
#endif
extern char **environ;
long _stksize=16384;
 
#if MINT
/* this is quite a bit MiNT library dependant... (although you
   don't want to use this when actually _running_ under MiNT ->
   use init.prg instead) */

#define USEARGV 1
/* ...especially it depends on system() no relying on $SHELL! */

#include <unistd.h>

int xsystem(const char *cmd)
{
	static int ret;

	/* GEMDOS redirection bugs department... */
	switch (vfork())
	{
	  case -1:						/* error */
		ret = -1;
		break;

	  case 0:						/* child */
		ret = system(cmd);
		exit(0);

	  default:						/* parent */
		wait((int *) NULL);
	}

	return ret;
}
#endif

#if USEARGV
#define INITBUFSIZ 0x400
#include <errno.h>

size_t bufsiz = INITBUFSIZ;
char *buf = (char *) NULL;

#ifndef __STDC__
extern char *putenv(), *realloc();
#endif

#else

#define	MAXENV	50

struct
{
	char *name;
	char *value;
} myenv[MAXENV];
#endif

int cmd_set(), cmd_exit(), cmd_cd(), cmd_pwd();
char home[FILENAME_MAX];

struct buildins
{
	char *name;
	int (*func)();
} buildins[]=
{	"exit", cmd_exit,
	"set", cmd_set,
	"cd", cmd_cd,
	"pwd", cmd_pwd,
	0,
};
  
#if USEARGV
char *getbuf(needed)
	size_t needed;
{
	if (buf && bufsiz >= needed)
		return buf;
	while (bufsiz < needed)
		bufsiz *= 2;
	if (!(buf = realloc(buf, bufsiz)))
		exit(-ENOMEM);
	return buf;
}

char *bufgets(fp)
	FILE *fp;
{
	int c = EOF;
	size_t p = 0, left = bufsiz-1;

	while(((c = getc(fp)) != EOF)) {
		if (!--left) {
			buf = getbuf(bufsiz * 2);
			left = bufsiz-p-1;
		}
		if((buf[p++] = c) == '\n')
			break;
	}
	buf[p] = '\0';
	if (!p && c == EOF)
		return NULL;
	return buf;
}
#endif

main(argc, argv)
	int argc;
	char **argv;
{
	int i;
#if USEARGV
	size_t p, clen;
	int interactive = isatty(0);
	char *ch;

	(void) getbuf((size_t) 1);
#else
	char buf[128];

	for (i=0; environ[i] && strncmp(environ[i],"ARGV=",5); i++)
		cmd_set(environ[i]);
#endif
	if (ch = getenv("HOME")) {
		strcpy(home, ch);
		ch = buf + strlen(strcpy(buf, ch));
		if (!strchr("\\/", ch[-1]))
			*ch++ = '/';
		strcpy(ch, "profile.sh");
		script(buf);
	} else {
		getcwd(home, sizeof(home));
		script("profile.sh");
	}

	if (argc>1 && !strcmp(argv[1], "-c"))
	{
		buf[0]='\0';
#if USEARGV
		p = 0;
		for (i=2; i<argc; i++)
		{	if (i>2) {
				buf[p++] = ' ';
				buf[p] = '\0';
			}
			strcpy(getbuf(p+(clen=strlen(argv[i]))+2)+p, argv[i]);
			p += clen;
		}
		i = execute(buf);
		exit(i>=0 ? i : errno);
#else
		for (i=2; i<argc; i++)
		{	if (i>2)
				strcat(buf, " ");
			strcat(buf, argv[i]);
		}
		execute(buf);
#endif
	}
	else
#if USEARGV
		while ((interactive ? fputs("$ ", stdout) : 0),
		       bufgets(stdin)) {
			if ((ch=strchr(buf, '\n')) != 0)
				*ch='\0';
			errno = 0;
			if (execute(buf) == -1 && interactive) {
				if (errno == ENOENT)
					printf("%s: not found\n", strtok(buf, " "));
				else if (errno == ENOEXEC)
					printf("%s: unable to exec\n", strtok(buf, " "));
			}
		}
#else
		while (fputs("$ ", stdout), gets(buf))
			execute(buf);
#endif
	exit(0);
}

execute(buf)
	char *buf;
{
	char *scan=buf;
#if !USEARGV
	char cmd[FILENAME_MAX];
	char line[128];
	char env[4096], *ep=env;
#endif
	int i;

	while (*scan==' ')
		scan++;
	if (!*scan)
		return 0;
	while (*scan && *scan!=' ')
		scan++;
	if (*scan)
		*scan++='\0';

	for (i=0; buildins[i].name; i++)
		if (!strcmp(buf, buildins[i].name))
			return (*buildins[i].func)(scan);

#if USEARGV
	if (*scan && !scan[-1])
		scan[-1] = ' ';
	return xsystem(buf);
#else
	if (!searchpath(buf, cmd))
	{	printf("%s: not found\n", buf);
		return -1;
	}

	strcpy(line+1, scan);
	line[0]=strlen(scan);
	for (i=0; i<MAXENV && myenv[i].name; i++)
	{	strcpy(ep, myenv[i].name);
		strcat(ep, "=");
		strcat(ep, myenv[i].value);
		ep+=strlen(ep)+1;
	}
	
	*ep='\0';

	return Pexec(0, cmd, line, env);
#endif
}

#if !USEARGV
searchpath(from, to)
	char *from, *to;
{
	char *path="";
	char *scan;
	char *end;
	char *q;
	int i;

	for (i=0; i<MAXENV && myenv[i].name; i++)
		if (!strcmp(myenv[i].name,"PATH"))
			path=myenv[i].value;
	for (scan=from; *scan; scan++)
		if (*scan==':' || *scan=='\\')
		{	path=0;
			break;
		}
	if (!path)
	{	strcpy(to, from);
		end=to+strlen(to);
		strcpy(end, ".prg"); if (try(to)) return 1;
		strcpy(end, ".ttp"); if (try(to)) return 1;
		strcpy(end, ".tos"); if (try(to)) return 1;
		*to='\0'; return 0;
	}
	for (scan=path; *scan; )
	{
		for (q=to; *scan && *scan!=';' && *scan!=','; scan++)
			*q++=*scan;
		if (*scan==';' || *scan==',')
			scan++;
		*q++='\\';
		*q='\0';
		strcpy(q, from);
		end=q+strlen(q);
		strcpy(end, ".prg"); if (try(to)) return 1;
		strcpy(end, ".ttp"); if (try(to)) return 1;
		strcpy(end, ".tos"); if (try(to)) return 1;
	}
	*to='\0';
	return 0;
}

try(name)
	char *name;
{
	if (Fattrib(name, 0, 0) < 0)
		return 0;
	return 1;
}
#endif

cmd_cd(line)
	char *line;
{
	if (!*line)
		line = home;
	else
		line = strtok(line, " ");
	if (chdir(line) < 0) {
		perror(line);
		return 1;
	}
	return 0;
}

cmd_pwd()
{
	char cwd[FILENAME_MAX];

	getcwd(cwd, (int) sizeof(cwd));
	puts(cwd);
	return 0;
}

cmd_exit()
{
	exit(0);
}

cmd_set(line)
	char *line;
{
	char *value;
#if USEARGV
	char **ep;
#else
	int i;
#endif

	if (!*line)
	{
#if USEARGV
		if (environ)
			for (ep = environ; *ep; ++ep) {
				fputs(*ep, stdout);
				fputs("\n", stdout);
			}
#else
		for (i=0; i<MAXENV && myenv[i].name; i++)
			printf("%s=%s\n", myenv[i].name, myenv[i].value);
#endif
		return 0;
	}

	for (value=line; *value && *value!='='; value++)
		;
	if (!*value)
	{	printf("Usage: set name=var\n");
		return -1;
	}
#if USEARGV
	if (!(line = strdup(line)))
		exit(-ENOMEM);
	return putenv(line);
#else
	*value++='\0';
	return doset(line, value);
#endif
}

#if !USEARGV
doset(line, value)
	char *line, *value;
{
	int i;

	for (i=0; i<MAXENV && myenv[i].name && strcmp(myenv[i].name, line); i++)
		;
	if (i==MAXENV)
	{	printf("No Space\n");
		return -1;
	}
	if (!myenv[i].name)
	{	myenv[i].name=malloc(strlen(line)+1);
		strcpy(myenv[i].name, line);
	}
	if (myenv[i].value)
		free(myenv[i].value);
	myenv[i].value=malloc(strlen(value)+1);
	strcpy(myenv[i].value, value);
	return 0;
}
#endif

script(name)
	char *name;
{
	FILE *fp;
#if USEARGV
	char *p;
#else
	char buf[128], *p;
#endif

	if ((fp=fopen(name, "r"))==0)
		return;
#if USEARGV
	while (bufgets(fp))
#else
	while (fgets(buf, (int) sizeof buf, fp))
#endif
	{
		if ((p=strchr(buf, '\n'))!=0)
			*p='\0';
		execute(buf);
	}
	fclose(fp);
}
