
/*
 * FEXEC1.C
 *
 *  (C)CopyRight 1987 Matthew Dillon, All rights reserved.
 *
 *    wait() and fexecv()
 *
 *    This code originated from Manx's fexecv code.  I claim credit only
 *    for the major modifications I've done to it.
 */

#include "shell.h"
#include <libraries/dosextens.h>

typedef struct CommandLineInterface CLI;
typedef struct FileHandle FH;

static int ret_val;

wait()
{
    return(ret_val);
}


fexecv(cmd, argv, stdin_str, stdout_str, stdout_append)
char *cmd, **argv;
char *stdin_str, *stdout_str;
{
    register CLI *cli;
    register char **ap, *cp, *arg;
    PROC *pp;
    APTR sav_ret;
    BPTR sav_CIS, sav_COS;
    long save_stdin_buf, save_stdin_pos, save_stdin_end;
    long len, seg, sav_seg;
    long openmode;
    long *stk;
    FH *fhp, *stdin, *stdout;
    char buf[40];

    pp = FindTask(0L);
    if ((cli = (CLI *)((long)pp->pr_CLI << 2)) == 0)
	return(-1);
    if ((seg = LoadIt(cmd)) == 0)
	return(-3);
    stdin = (FH *)((stdin_str)? Open(stdin_str, 1005) : pp->pr_CIS);
    if (!stdin) {
	fhprintf(Cerr, "Input redirection error\n");
	return(-4);
    }
    openmode = (stdout_append) ? 1005 : 1006;
    stdout= (FH *)((stdout_str)? Open(stdout_str, openmode) : pp->pr_COS);
    if (!stdout) {
	fhprintf(Cerr, "Output redirection error\n");
	if (stdin_str)
	    Close(stdin);
	return(-5);
    }
    if (stdout_append)
	Seek(stdout, 0, 1);
    sav_seg = cli->cli_Module;
    cli->cli_Module = seg;
    stk = (long *)AllocMem(4 * cli->cli_DefaultStack + 8, 0);
    *stk = 4 * cli->cli_DefaultStack + 8;
    stk = (long *)((long)stk + 4 * cli->cli_DefaultStack);
    stk[0] = 4 * cli->cli_DefaultStack;
    stk[1] = ((long *)pp->pr_ReturnAddr)[1];
    sav_ret = pp->pr_ReturnAddr;
    pp->pr_ReturnAddr = (APTR)stk;

    for (len = 1, ap = argv + 1; *ap; ++ap)  /* length of command line */
	len += strlen(*ap) + 1;
    cp = arg = malloc(len + 1);
    for (ap = argv + 1; *ap; ++ap) {
	strcpy(cp, *ap);
	strcat(cp, " ");
	cp += strlen(cp);
    }
    if (len > 199)                           /* BCPL parameter limit   */
	len = 199;
    arg[len-1] = '\n';                       /* for BCPL               */
    arg[len]   = 0;			     /* for C		       */
    cp = (char *)((long)cli->cli_CommandName << 2);
    movmem(cp, buf, 40);
    strcpy(cp + 1, cmd);
    cp[0] = strlen(cmd);

    fhp = (FH *)((long)stdin << 2);
    save_stdin_buf = fhp->fh_Buf;
    save_stdin_pos = fhp->fh_Pos;
    save_stdin_end = fhp->fh_End;

    fhp->fh_Buf = (long)AllocMem(202, 0) >> 2;
    bmov(arg, fhp->fh_Buf<<2, len);
    fhp->fh_Pos = 0;
    fhp->fh_End = len;

    sav_CIS = pp->pr_CIS;
    sav_COS = pp->pr_COS;

    pp->pr_CIS = (BPTR)stdin;
    pp->pr_COS = (BPTR)stdout;

    /*
     *	pr_Result2 must be NULL or RUN/NEWCLI think the command line is
     *	somewhere other than in the file handle.  The cli_Interactive
     *	field gets cleared sometimes (how???), and for some reason, signal
     *	31 is set sometimes and might cause inproper operation of RUN.
     */

    pp->pr_Result2 = NULL;
    cli->cli_Interactive = -1;
    SetSignal(0L, 0x80000000);

    ret_val = doexec(len, arg, (seg+1)<<2, stk);

    FreeMem(fhp->fh_Buf<<2, 202);
    fhp->fh_Buf = save_stdin_buf;
    fhp->fh_Pos = save_stdin_pos;
    fhp->fh_End = save_stdin_end;

    if (stdin_str)
	Close(stdin);
    if (stdout_str)
	Close(stdout);
    pp->pr_CIS = sav_CIS;
    pp->pr_COS = sav_COS;

    UnLoadSeg(cli->cli_Module);
    pp->pr_ReturnAddr = sav_ret;
    cli->cli_Module = sav_seg;
    free(arg);
    movmem(buf, cp, 40);
    return(0);
}

LoadIt(cmd)
char *cmd;
{
    long seg;

    mountrequest(0);
    if ((seg = LoadSeg(cmd)) == NULL) {
	register long lock;
	char buf[128];

	if (lock = FindIt(cmd, "", buf)) {
	    register long pardir = ParentDir(lock);
	    if (pardir) {
		register long oldir = CurrentDir(pardir);
		seg = LoadSeg(cmd);
		CurrentDir(oldir);
		UnLock(pardir);
	    }
	    UnLock(lock);
	}
    }
    mountrequest(1);
    return(seg);
}

/*
 *  Find a specific command (cmd) with extension (ext).  Returns
 *  a lock if found, else NULL.  Searches both the symbolic path and
 *  the CLI path.  The symbolic path is searched first.
 */

long
FindIt(cmd, ext, buf)
char *cmd;
char *ext;
char *buf;
{
    register long lock = 0;
    register char *p;
    PROC *myproc = FindTask(0);

    strcpy(buf, cmd);
    strcat(buf, ext);
    if (rindex(buf, ':') || rindex(buf, '/'))
	return(Lock(buf, ACCESS_READ));

    if ((p = get_var(LEVEL_SET, V_PATH)) == NULL)
	p = "";

#ifdef DEBUG
    if (SDebug)
	printf ("FindIt: try: %s\n", buf);
#endif
    while ((lock = Lock(buf, ACCESS_READ)) == 0 || !isfile(lock)) {
	register short n;
	if (lock)
	    UnLock(lock);
	if (*p == '\0') {
	    buf[0] = 0;
	    break;
	}
	for (n = 0; p[n] && p[n] != ','; ++n);
	strncpy(buf, p, n);
	buf[n] = 0;
	strcat(buf, cmd);
	strcat(buf, ext);
	p += n + (*p != 0);
#ifdef DEBUG
	if (SDebug)
	    printf ("FindIt: try: %s\n", buf);
#endif
    }
#ifdef DEBUG
    if (SDebug)
	puts ("FindIt 1");
#endif
    if (lock)
	return(lock);

    /*
     *	Search the CLI path by CurrentDir'ing each lock and
     *	attempting to lock the name
     */

#ifdef DEBUG
    if (SDebug)
	puts("SEARCH CLI");
#endif

    strcpy(buf, cmd);
    strcat(buf, ext);
    if (myproc->pr_CLI) {
	long oldir;
	long dupdir;
	register LOCK *dirlock = (LOCK *)(((CLI *)(myproc->pr_CLI << 2))->cli_CommandDir << 2);
	for (; dirlock; dirlock = (LOCK *)(dirlock->fl_Link << 2)) {
	    oldir = CurrentDir(((long *)dirlock)[1]);
	    lock = Lock(buf, ACCESS_READ);
	    CurrentDir(oldir);
	    if (lock)
		break;
	}
    }

#ifdef DEBUG
    if (SDebug)
	puts("FindIt done");
#endif
    return(lock);
}

static
isfile(lock)
long lock;
{
    register FIB *fib = (FIB *)malloc(sizeof(FIB));
    register int result = 0;

    if (fib) {
	if (Examine(lock, fib))
	    result = fib->fib_DirEntryType;
	free(fib);
    }
    return(result < 0);         /*  is it a file?   */
}

