/*
**	Lisp.m
**	Lisp client object with message-based I/O.
**	Lee Boynton, NeXT, Inc., 1989
*/

#import <mach.h>
#import <stdlib.h>
#import <string.h>
#import <fcntl.h>
#import <signal.h>
#import <sys/wait.h>
#import <dpsclient/dpsclient.h>
#import <appkit/defaults.h>
#import <appkit/Application.h>
#import <objc/List.h>
#import "Lisp.h"

@implementation Lisp

#define READ_BUF_SIZE (8191)
static char read_buf[READ_BUF_SIZE+1];

static void *read_lisp(int fd, Lisp *self)
{
    extern int read();
    NXEvent event;
    int count = read(fd,read_buf,READ_BUF_SIZE);
    id delegate = self->delegate;
    BOOL tellDelegate = NO;
    if (delegate  && [delegate respondsTo:@selector(lispOutput:)])
	tellDelegate = YES;
    while (count > 0) {
	read_buf[count] = '\0';
	if (tellDelegate)
	    [delegate lispOutput:(const char *)read_buf];
	if ([NXApp peekNextEvent:-1 into:&event waitFor:0.0 threshold:1])
	    break;
	count = read(fd,read_buf,READ_BUF_SIZE);
    }
}

static int launch(const char *name, const char **argv, 
		  int *child_pid,
		  int *write_child_fd, int *read_child_fd)
{
    extern int pipe();
    extern int fork();
    extern int dup2();
    extern int close();
    extern int execvp();
    extern int fcntl();
    extern int close();
    int err, pid;
    int p_to_child[2], p_from_child[2];

    if ((err = pipe(p_to_child)) < 0)
	return err;
    if ((err = pipe(p_from_child)) < 0) {
	close(p_to_child[0]);
	close(p_to_child[1]);
	return err;
    }
    if ((pid = fork()) == 0) {
	dup2(p_to_child[0], 0);
	dup2(p_from_child[1], 1);
	dup2(p_from_child[1], 2);
	close(p_to_child[1]);
	close(p_from_child[0]);
	execvp(name, argv);
	exit(127);
    }
    if (pid == -1) {
	close(p_to_child[0]);
	close(p_to_child[1]);
	close(p_from_child[0]);
	close(p_from_child[1]);
	return -1;
    }
    *child_pid = pid;
    *write_child_fd = p_to_child[1];
    close(p_to_child[0]);
    *read_child_fd = p_from_child[0];
    close(p_from_child[1]);
    return fcntl(*read_child_fd,F_SETFL,FNDELAY);
}


static id lispList = nil;

static id findLispForPid(int pid) {
    Lisp *self;
    int i, max = [lispList count];
    for (i=0; i<max; i++) {
	self = (Lisp *)[lispList objectAt:i];
	if (self->lisp_pid == pid)
	    return self;
    }
    return nil;
}

static void lispDied(int ignore) {
    union wait epitaph;
    int pid;
    Lisp *self;
    
    while (1) {
	pid = wait3(&epitaph,WNOHANG,0);
	if (pid > 0) {
	    if (self = findLispForPid(pid)) {
		self->lisp_pid = 0;
		break;
	    }
	} else
	    break;
    }
    signal(SIGCHLD,&lispDied);
}

+ initialize
{
    if (!lispList)
	lispList = [List new];
    signal(SIGCHLD,&lispDied);
    return self;
}

+ new
{
    int err;
    const char *argv[2];
    const char *imageName = NXGetDefaultValue([NXApp appName],"LispImage");
    self = [super new];
    argv[0] = imageName;
    argv[1] = NULL;
    [lispList addObject:self];
    err = launch(imageName,argv,&lisp_pid, &lisp_in_fd,&lisp_out_fd);
    if (err) {
	[self free];
	return nil;
    }
    DPSAddFD(lisp_out_fd,(DPSFDProc)read_lisp,(void *)self,1);
    return self;
}

- free
{
    extern int kill();
    DPSRemoveFD(lisp_out_fd);
    [lispList removeObject:self];
    if (lisp_pid)
	kill(lisp_pid,9);
    return [super free];
}

- setDelegate:anObject
{
    delegate = anObject;
    return self;
}

- delegate
{
    return delegate;
}

- inputLisp:(const char *)theString
{
    extern int write();
    int i, count = strlen(theString);
    if (!lisp_pid || (count != write(lisp_in_fd,theString,count)))
	return nil;
    return self;
}

- interruptLisp
{
    extern int kill();
    if (lisp_pid && kill(lisp_pid,2))
	return nil;
    return self;
}

@end









