#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "newt.h"
#include "log.h"
#include "run.h"
#include "windows.h"

int runProgramRoot(enum runType runType, char * root, char * name, 
		   char ** args) {
    return runProgramIORoot(runType, root, name, args, NULL, NULL);
}

int runProgramIO(enum runType runType, char * name, char ** args, char * in,
		 char ** out) {
    return runProgramIORoot(runType, NULL, name, args, in, out);
}

int runProgramIORoot(enum runType runType, char * root, char * name, 
		char ** args, char * in, char ** out) {

    char * buf;
    int infd, outfd, errfd, status, i;
    int resultfd = 0;
    pid_t pid;
    char ** currarg;
    int inputPipe[2], outputPipe[2];
    char resultbuf[200];
    char * result;
    int resultSize;
    char fullname[200];

    /* if "in" is set, it better be short. We assume one write() call is
       enough */

    if (root) {
	sprintf(fullname, "%s/%s", root, name);
    } else 
	strcpy(fullname, name);

    i = 0;
    i = strlen(name) + 50;
    
    currarg = args;
    while (*currarg) {
	i += strlen(*currarg) + 1;
	currarg++;
    }

    buf = alloca(i);

    if (testing) 
	strcpy(buf, "if I weren't testing I would run:\n\n");
    else
	strcpy(buf, "running: ");

    strcat(buf, name);
    strcat(buf, " ");
    
    currarg = args;
    while (*currarg) {
	strcat(buf, *currarg);
	strcat(buf, " ");
	currarg++;
    }

    if (testing) {
	newtComponent t, f, succeed, fail;

	newtCenteredWindow(45, 15, "Running");

	succeed = newtButton(8, 10, "Succeed");
	fail = newtButton(28, 10, "Fail");
	t = newtTextbox(2, 1, 40, 13, NEWT_TEXTBOX_WRAP | NEWT_TEXTBOX_SCROLL);
	newtTextboxSetText(t, buf);
	f = newtForm(NULL, NULL, 0);

	newtFormAddComponents(f, t, succeed, fail, NULL);

	t = newtRunForm(f);
     
	newtFormDestroy(f);
	newtPopWindow();

	return t == fail;
    } 

    if (access(fullname,  X_OK))  {
	logMessage("cannot run %s: %s", fullname, strerror(errno));
	newtWinMessage("Error", "Ok", "I cannot run %s: %s", fullname, 
			    strerror(errno));
	return -1;
    }
    

    logMessage(buf);

    if (root)
	logMessage("    root is %s", root);

    if (in) {
	pipe(inputPipe);
	write(inputPipe[1], in, strlen(in));
	close(inputPipe[1]);
	infd = inputPipe[0];
    } else {
	infd = open("/dev/null", O_RDONLY);
    }

    errfd = open("/dev/tty5", O_APPEND | O_CREAT);
    if (errfd < 0)
	errfd = open("/tmp/exec.log", O_APPEND | O_CREAT);

    if (out) {
	pipe(outputPipe);
	outfd = outputPipe[1];
	resultfd = outputPipe[0];
    } else if (runType & RUN_LOG) {
	outfd = open("/dev/tty5", O_RDWR);
	if (outfd < 0)
	    outfd = open("/tmp/exec.log", O_APPEND | O_CREAT);
    } else
	outfd = open("/dev/null", O_RDWR);

    if (!(pid = fork())) {
	close(0);
	close(1);
	close(2);
	
	if (root) {
	    chroot(root);
	    chdir("/");
	}

	dup2(infd, 0);
	dup2(outfd, 1);
	dup2(errfd, 2);

	close(infd);
	close(outfd);
	close(errfd);
	if (out) close(resultfd);

	execv(name, args);
	logMessage("exec of %s failed: %s", name, strerror(errno));
	exit(-1);
    }

    close(infd);
    close(outfd);
    close(errfd);

    if (out) {
	resultSize = 0;
	result = NULL;

	do { 
	    i = read(resultfd, resultbuf, sizeof(resultbuf));

	    if (!result) {
		result = malloc(i + 1);
	    } else 
		result = realloc(result, resultSize + i + 1);
	    memcpy(result + resultSize, resultbuf, i);
	    resultSize += i;
	    result[resultSize] = '\0';
	} while (i > 0);

        close(resultfd);
	*out = result;
    }

    waitpid(pid, &status, 0);

    if (WIFEXITED(status))
	return WEXITSTATUS(status);

    return -1;
}

int runProgram(enum runType runType, char * name, char ** args) {
    return runProgramIO(runType, name, args, NULL, NULL);
}