#include <sys/emx.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <malloc.h>
#include <process.h>

static int ansi = 0;

/* input.c */
extern int get_input(char *str, int size, int opt_newline);

static char **org_env;

static char *timestr( unsigned t, char *buf )
{
    sprintf( buf, "%2.2d:%02.2d", (t >> 11) & 0x1f, (t >> 5) & 0x3f);
    return buf;
}

static char *datestr( unsigned d, char *buf )
{
    sprintf( buf, "%2.2d-%2.2d-%2.2d", d&0x1f, (d>>5) & 0x0f, (d>>9) + 80 );
    return buf;
}

static long show_filename(struct _find *pfind)
{
    char timebuf[10], datebuf[10];
    unsigned size = ((unsigned) pfind->size_hi << 16) + pfind->size_lo;

    datestr( pfind->date, datebuf );
	timestr( pfind->time, timebuf );

    if (pfind->attr & (_A_SUBDIR | _A_VOLID))
	printf( "%-15s  <DIR>\n", pfind->name);
    else
	printf( "%-15s %8u %8s %8s\n",
	    pfind->name, size, datebuf, timebuf);
    return size;
}

typedef enum {
    DEFAULT, SET, ORGENV, DIR, CD, DRIVE, ECHO, CLS, DEL, HELP, EXIT
} commands;

typedef struct {
    char *cp;
    commands t;
} item;

static item cmds[] =
{
    "set", SET,
    "orgenv", ORGENV,
    "dir", DIR,
    "cd", CD,
    "drive", DRIVE,
    "echo", ECHO,
    "del", DEL,
    "help", HELP,
    "exit", EXIT,
    "cls", CLS,
    NULL, 0
};

static int execute_command(char *argv[])
{
    int i;
    static char wild[]="*.*";
    int cmd = 0;

    for (i = 0; cmds[i].cp; i++)
	if (stricmp(cmds[i].cp, argv[0]) == 0) {
	    cmd = cmds[i].t;
	    break;
	}
    if (!cmd)
	cmd = DEFAULT;

    switch (cmd) {
    case SET:
	if (argv[1]) {
	    char *e = malloc(strlen(argv[1]) + 2);
	    if (!e) return 0;
	    strcpy(e, argv[1]);
	    strupr(e);
	    if (putenv(e)) {
		puts("Bad environmemt");
		free(e);
	    }
	}
	else
	    for (i=0; environ[i] != NULL; i++)
		puts(environ[i]);
	break;

    case ORGENV:
	environ = org_env;
	break;

    case DIR:
	{
	struct _find info;
	if (argv[1]==0)
	    argv[1]=wild;
	if (__findfirst(argv[1],_A_NORMAL | _A_SUBDIR | _A_RDONLY, &info)) {
	    puts("no files found");
	    return 0;
	}
	show_filename(&info);
	while (__findnext(&info) == 0)
	    show_filename(&info);
	}
	break;

    case CD:
	if (argv[1])
	    _chdir2(argv[1]);
	break;

    case DRIVE:
	if (argv[1])
	    _chdrive(argv[1][0]);
	break;

    case ECHO:
	break;

    case CLS:
	printf("\033[2J");
	printf("\033[H");
	break;

    case DEL:
	if (! argv[1])
	    return 0;
	else if (strpbrk(argv[1],"*?")) {
	    struct _find info;
	    if (__findfirst(argv[1], _A_NORMAL, &info)) {
		puts("no files found");
		return 0;
	    }
	    else remove(info.name);
	    while (__findnext(&info) == 0)
		remove(info.name);
	}
	else remove(argv[1]);
	break;

    case HELP:
	if (ansi)
	    printf("\033[1;36m");
	puts("shell commands: ");
	puts("    set   : set environment");
	puts("    orgenv: restore orginal environment");
	puts("    cd    : change path");
	puts("    drive : change drive");
	puts("    dir   : list directory");
	puts("    del   : delete files");
	puts("    cls   : clear screen");
	puts("    exit  : quit shell");
	puts("    help  : this text");
	puts("    EMX prgs and .bat prgs");
	puts("command-line: ");
	puts("    Up    : last command");
	puts("    Home  : goto begin command");
	puts("    END   : goto end of command");
	puts("    ESC   : clear command");
	if (ansi)
	    printf("\033[0m");
	break;

    case EXIT:
	exit(1);

    case DEFAULT:
	if (strlen(argv[0]) == 2 && argv[0][1] == ':') {
	    _chdrive(argv[0][0]);
	    break;
	}

	if (!strstr(argv[0], ".bat")) {
	    unsigned int status;
	    if (spawnvpe(P_NOWAIT, argv[0], argv, environ) != -1)
		wait(&status);
	    putchar('\n');
	    return 0;
	}
    } /* switch */

    return 0;
}

int readline(FILE *fhandle, char *linebuf)
{
    int ch;
    int i = 0;

    if (fhandle == stdin)
	return get_input(linebuf, 512, 1);

    for (;;) {
	ch = fgetc(fhandle);
	if (ch == EOF) {
	    if (i == 0)
		return -1;
	    linebuf[i] = 0;
	    return i;
	}
	else if (ch == '\n' || ch == '\r') {
	    linebuf[i] = 0;
	    return i;
	}
	else if (ch=='\t' || ch=='@') {
	    linebuf[i++]=' ';
	}
	else
	    linebuf[i++] = ch;
    }
}

/* build argv from string */
int make_tokens(char *string, char *argv[])
{
    int argc,i = 0;
    argv[0] = NULL;

    while (string[i]==' ')
	i++;
    if (string[i] == 0 || string[i] == '\n')
	return 0;

    argc = 1;
    argv[0] = string + i;
    for (i = 0; string[i] != 0; i++)
	if (string[i] == ' ') {
	    string[i] = 0;
	    if (string[i+1] == ' ')
		continue;
	    if (string[i+1] == 0)
		break;
	    argv[argc++] = string + i + 1;
	}
    argv[argc] = 0;
    return argc;
}

#define RUN_RSX 	0x1000L

static void print_version(void)
{
    if (_emx_env & RUN_RSX) {
	if (_emx_rev >> 16) {
	    printf("\033[31m This shell is running under RSXWIN , version %d",
		(_emx_rev & 0xFFFF) >> 8);
	    printf("\033[0m\n");
	} else
	    printf("This shell is running under RSX , version %lX\n", _emx_rev);
    } else
	printf("This shell is running under EMX , revision %lX\n", _emx_rev);
}

int main(int argc, char **argv)
{
    char cline[512];
    char *argvec[64];

    org_env = environ;

    if (getenv("TERM"))
	ansi = 1;

    print_version();

    if (argv[1])
	execute_command(argv+1);
    else for (;;) {
	cline[0] = _getdrive();
	cline[1] = ':';
	getcwd(cline+2, 256);
	strlwr(cline);

	if (ansi)
	    printf("\033[1;32m");
	printf("%s> ", cline);
	if (ansi)
	    printf("\033[1;31m");
	if (readline(stdin, cline) < 0)
	    continue;
	if (ansi)
	    printf("\033[0;1m");

	if (!make_tokens(cline, argvec))
	    continue;
	if (strstr(argvec[0], ".bat")) {
	    FILE *fd = fopen(cline, "rt");
	    if (!fd)
		continue;
	    while (readline(fd, cline) >= 0) {
		if (!make_tokens(cline, argvec))
		    continue;
		execute_command(argvec);
	    }
	    fclose(fd);
	}
	else
	    execute_command(argvec);
    }
    return 0;
}
