/*
 * exec.c: handles exec'd process for IRCII 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include "irc.h"
#include <sys/wait.h>
#ifdef ISC22
#include <sys/bsdtypes.h>
#endif /* ISC22 */
#ifdef XD88
#define ISC22
#endif
/* #if defined(ESIX) || defined(XD88) /* */
#if defined(ESIX)
#include <lan/net_types.h>
#endif /* ESIX */
/* #endif /* ESIX || XD88 */
#ifdef POSIX
# include <sys/select.h>
#endif /* POSIX */
#include "exec.h"
#include "vars.h"
#include "ircaux.h"
#include "edit.h"
#include "window.h"
#include "screen.h"
#include "hook.h"
#include "input.h"
#include "list.h"
#include "server.h"
#include "output.h"

extern	int	dgets();
extern	void	dgets_timeout();

static	int	delete_process();
static	int	wait_index = -1;

extern	int	doing_privmsg;

/* Process: the structure that has all the info needed for each process */
typedef struct
{
	char	*name;			/* full process name */
	char	*logical;
	int	pid;			/* process-id of process */
	int	p_stdin;		/* stdin description for process */
	int	p_stdout;		/* stdout descriptor for process */
	int	p_stderr;		/* stderr descriptor for process */
	int	counter;		/* output line counter for process */
	char	*redirect;		/* redirection command (MSG, NOTICE) */
	unsigned int	refnum;		/* a window for output to go to */
	int	server;			/* the server to use for output */
	char	*who;			/* nickname used for redirection */
	int	exited;			/* true if process has exited */
	int	termsig;		/* The signal that terminated
					 * the process */
	int	retcode;		/* return code of process */
	List	*waitcmds;		/* commands queued by WAIT -CMD */
}	Process;

static	Process **process_list = null(Process **);
static	int	process_list_size = 0;

/*
 * A nice array of the possible signals.  Used by the coredump trapping
 * routines and in the exec.c package 
 */
char	*signals[] =
{
	null(char *), "HUP", "INT", "QUIT",
	"ILL", "TRAP", "IOT", "EMT",
	"FPE", "KILL", "BUS", "SEGV",
	"SYS", "PIPE", "ALRM", "TERM",
	"URG", "STOP", "TSTP", "CONT",
	"CHLD", "TTIN", "TTOU", "IO",
	"XCPU", "XFSZ", "VTALRM", "PROF",
	"WINCH", "LOST", "USR1", "USR2"
};

/*
 * exec_close: silly, eh?  Well, it makes the code look nicer.  Or does it
 * really?  No.  But what the hell 
 */
int	exec_close(des)
int	des;
{
	if (des != -1)
		new_close(des);
	return (-1);
}

/*
 * set_wait_process: Sets the given index number so that it will be checked
 * for upon process exit.  This is used by waitcmd() in edit.c.  An index of
 * -1 disables this.
 */
void	set_wait_process(index)
int	index;
{
	wait_index = index;
}

/*
 * valid_process_index: checks to see if index refers to a valid running
 * process and returns true if this is the case.  Returns false otherwise 
 */
static	int	valid_process_index(index)
int	index;
{
	if ((index < 0) || (index >= process_list_size))
		return (0);
	if (process_list[index])
		return (1);
	return (0);
}

/*
 * check_wait_status: This waits on child processes, reporting terminations
 * as needed, etc 
 */
void	check_wait_status()
{
#if !defined(ISC22) && !defined(SVR4) && !defined(POSIX) && !defined(__386BSD__)
	union wait status;
#else
	int	status;
#endif /* !defined(ISC22) && !defined(SVR4) */
	int	pid,
		i;

#if defined(use_wait2) || defined(MUNIX)
	if ((pid = wait2(&status, WNOHANG, 0)) > 0)
	{
#else
# if defined(ISC22) || defined(SVR4) || defined(POSIX) || defined(__386BSD__)
	if ((pid = waitpid(-1, &status, WNOHANG)) > 0)
	{
# else
	if ((pid = wait3(&status, WNOHANG, NULL)) > 0)
	{
# endif /* defined(ISC22) || defined (SVR4) */
#endif /* defined(use_wait2) || defined(MUNIX) */
		errno = 0;	/* reset errno, cause wait3 changes it */
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i] && (process_list[i]->pid == pid))
			{
				process_list[i]->exited = 1;
#if !defined(ISC22) && !defined(SVR4) && !defined(POSIX) && !defined(linux) && !defined(__386BSD__)
				process_list[i]->termsig = status.w_T.w_Termsig;
				process_list[i]->retcode = status.w_T.w_Retcode;
#else
				process_list[i]->termsig = WTERMSIG(status);
				process_list[i]->retcode = WEXITSTATUS(status);
#endif /* !defined(ISC22) && !defined(SVR4) */
				if ((process_list[i]->p_stderr == -1) &&
				    (process_list[i]->p_stdout == -1))
					delete_process(i);
				return;
			}
		}
#if 0	/* to match the braces above */
	}
	}
#endif
	}
}

/*
 * check_process_limits: checks each running process to see if it's reached
 * the user selected maximum number of output lines.  If so, the processes is
 * effectively killed 
 */
void	check_process_limits()
{
	int	limit;
	int	i;

	if ((limit = get_int_var(SHELL_LIMIT_VAR)) && process_list)
	{
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i])
			{
				if (process_list[i]->counter >= limit)
				{
					process_list[i]->p_stdin = exec_close(process_list[i]->p_stdin);
					process_list[i]->p_stdout = exec_close(process_list[i]->p_stdout);
					process_list[i]->p_stderr = exec_close(process_list[i]->p_stderr);
					if (process_list[i]->exited)
						delete_process(i);
				}
			}
		}
	}
}

/*
 * do_processes: given a set of read-descriptors (as returned by a call to
 * select()), this will determine which of the process has produced output
 * and will hadle it appropriately 
 */
void	do_processes(rd)
fd_set	*rd;
{
	int	i,
		flag;
	char	exec_buffer[INPUT_BUFFER_SIZE + 1];
	char	*ptr;

	if (process_list == null(Process **))
		return;
	dgets_timeout(1L);
	for (i = 0; i < process_list_size && !break_io_processing; i++)
	{
		if (process_list[i] && (process_list[i]->p_stdout != -1))
		{
			if (FD_ISSET(process_list[i]->p_stdout, rd))
			{

	/*
	 * This switch case indented back to allow for 80 columns,
	 * phone, jan 1993.
	 */
		switch (dgets(exec_buffer, INPUT_BUFFER_SIZE,
				process_list[i]->p_stdout))
		{
		case 0:
			if (process_list[i]->p_stderr == -1)
			{
				process_list[i]->p_stdin =
					exec_close(process_list[i]->p_stdin);
				process_list[i]->p_stdout =
					exec_close(process_list[i]->p_stdout);
				if (process_list[i]->exited)
					delete_process(i);
			}
			else
				process_list[i]->p_stdout =
					exec_close(process_list[i]->p_stdout);
			break;
		case -1:
			if (process_list[i]->logical)
				flag = do_hook(EXEC_PROMPT_LIST, "%s %s",
					process_list[i]->logical, exec_buffer);
			else
				flag = do_hook(EXEC_PROMPT_LIST, "%d %s", i,
					exec_buffer);
			set_prompt_by_refnum(process_list[i]->refnum,
				exec_buffer);
			update_input(UPDATE_ALL);
			/* if (flag == 0) */
			break;
		default:
			message_to(process_list[i]->refnum);
			process_list[i]->counter++;
			ptr = exec_buffer + strlen(exec_buffer) - 1;
			if ((*ptr == '\n') || (*ptr == '\r'))
			{
				*ptr = null(char);
				ptr = exec_buffer + strlen(exec_buffer) - 1;
				if ((*ptr == '\n') || (*ptr == '\r'))
					*ptr = null(char);
			}
			if (process_list[i]->logical)
				flag = do_hook(EXEC_LIST, "%s %s",
					process_list[i]->logical, exec_buffer);
			else
				flag = do_hook(EXEC_LIST, "%d %s", i,
					exec_buffer);
			if (flag)
			{
				if (process_list[i]->redirect)
				{
					int	server;

					server = from_server;
					from_server = process_list[i]->server;
					send_text(process_list[i]->who,
						exec_buffer,
						process_list[i]->redirect);
					from_server = server;
				}
				else
					put_it("%s", exec_buffer);
			}
			message_to(0);
			break;
		}

		/* end of special intendation */

			}
		}
		if (process_list && (i < process_list_size) &&
		    process_list[i] && (process_list[i]->p_stderr != -1))
		{
			if (FD_ISSET(process_list[i]->p_stderr, rd))
			{

	/* Do the intendation on this switch as well */

		switch (dgets(exec_buffer, INPUT_BUFFER_SIZE,
				process_list[i]->p_stderr))
		{
		case 0:
			if (process_list[i]->p_stdout == -1)
			{
				process_list[i]->p_stderr =
					exec_close(process_list[i]->p_stderr);
				process_list[i]->p_stdin =
					exec_close(process_list[i]->p_stdin);
				if (process_list[i]->exited)
					delete_process(i);
			}
			else
				process_list[i]->p_stderr =
					exec_close(process_list[i]->p_stderr);
			break;
		case -1:
			if (process_list[i]->logical)
				flag = do_hook(EXEC_PROMPT_LIST, "%s %s",
					process_list[i]->logical, exec_buffer);
			else
				flag = do_hook(EXEC_PROMPT_LIST, "%d %s", i,
					exec_buffer);
			set_prompt_by_refnum(process_list[i]->refnum,
				exec_buffer);
			update_input(UPDATE_ALL);
			if (flag == 0)
				break;
		default:
			message_to(process_list[i]->refnum);
			(process_list[i]->counter)++;
			ptr = exec_buffer + strlen(exec_buffer) - 1;
			if ((*ptr == '\n') || (*ptr == '\r'))
			{
				*ptr = null(char);
				ptr = exec_buffer + strlen(exec_buffer) - 1;
				if ((*ptr == '\n') || (*ptr == '\r'))
					*ptr = null(char);
			}
			if (process_list[i]->logical)
				flag = do_hook(EXEC_ERRORS_LIST, "%s %s",
					process_list[i]->logical, exec_buffer);
			else
				flag = do_hook(EXEC_ERRORS_LIST, "%d %s", i,
					exec_buffer);
			if (flag)
			{
				if (process_list[i]->redirect)
				{
					int	server;

					server = from_server;
					from_server = process_list[i]->server;
					send_text(process_list[i]->who,
						exec_buffer,
						process_list[i]->redirect);
					from_server = server;
				}
				else
					put_it("%s", exec_buffer);
			}
			message_to(0);
			break;
		}

	/* End of indentation for 80 columsn */

			}
		}
	}
	dgets_timeout(0L);
}

/*
 * set_process_bits: This will set the bits in a fd_set map for each of the
 * process descriptors. 
 */
void	set_process_bits(rd)
fd_set	*rd;
{
	int	i;

	if (process_list)
	{
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i])
			{
				if (process_list[i]->p_stdout != -1)
					FD_SET(process_list[i]->p_stdout, rd);
				if (process_list[i]->p_stderr != -1)
					FD_SET(process_list[i]->p_stderr, rd);
			}
		}
	}
}

/*
 * list_processes: displays a list of all currently running processes,
 * including index number, pid, and process name 
 */
static	void	list_processes()
{
	int	i;

	if (process_list)
	{
		say("Process List:");
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i])
			{
				if (process_list[i]->logical)
					say("\t%d (%s): %s", i,
						process_list[i]->logical,
						process_list[i]->name);
				else
					say("\t%d: %s", i,
						process_list[i]->name);
			}
		}
	}
	else
		say("No processes are running");
}

void	add_process_wait(index, cmd)
int 	index;
char 	*cmd;
{
	List	*new,
		**posn;

	for (posn = &process_list[index]->waitcmds;
	    *posn != null(List *); posn = &(*posn)->next);
	new = (List *) new_malloc(sizeof(List));
	*posn = new;
	new->next = null(List *);
	new->name = null(char *);
	malloc_strcpy(&new->name, cmd);
}

/*
 * delete_process: Removes the process specifed by index from the process
 * list.  The does not kill the process, close the descriptors, or any such
 * thing.  It only deletes it from the list.  If appropriate, this will also
 * shrink the list as needed 
 */
static	int	delete_process(index)
int	index;
{
	int	flag;
	List	*cmd,
		*next;

	if (process_list)
	{
		if (index >= process_list_size)
			return (-1);
		if (process_list[index])
		{
			Process *dead;

			if (index == wait_index)
			{
				wait_index = -1;
				irc_io_loop = 0;
			}
			dead = process_list[index];
			process_list[index] = null(Process *);
			if (index == (process_list_size - 1))
			{
				int	i;

				for (i = process_list_size - 1;
						process_list_size;
						process_list_size--, i--)
				{
					if (process_list[i])
						break;
				}
				if (process_list_size)
					process_list = (Process **)
						new_realloc(process_list,
						sizeof(Process *) *
						process_list_size);
				else
				{
					new_free(&process_list);
					process_list = null(Process **);
				}
			}
			for (next = dead->waitcmds; next;)
			{
				cmd = next;
				next = next->next;
				parse_command(cmd->name, 0, empty_string);
				new_free(&cmd->name);
				new_free(&cmd);
			}
			dead->waitcmds = null(List *);
			if (dead->logical)
				flag = do_hook(EXEC_EXIT_LIST, "%s %d %d",
					dead->logical, dead->termsig,
					dead->retcode);
			else
				flag = do_hook(EXEC_EXIT_LIST, "%d %d %d",
					index, dead->termsig, dead->retcode);
			if (flag)
			{
				if (get_int_var(NOTIFY_ON_TERMINATION_VAR))
				{
					if (dead->termsig)
						say("Process %d (%s) \
terminated with signal %s (%d)", index, dead->name, signals[dead->termsig],
							dead->termsig);
					else
						say("Process %d (%s) \
terminated with return code %d", index, dead->name, dead->retcode);
				}
			}
			new_free(&dead->name);
			new_free(&dead->logical);
			new_free(&dead->who);
			new_free(&dead->redirect);
			new_free(&dead);
			return (0);
		}
	}
	return (-1);
}

/*
 * add_process: adds a new process to the process list, expanding the list as
 * needed.  It will first try to add the process to a currently unused spot
 * in the process table before it increases it's size. 
 */
static	void	add_process(name, logical, pid, p_stdin, p_stdout, p_stderr,
		redirect, who, refnum)
char	*name,
	*logical;
int	pid,
	p_stdin,
	p_stdout,
	p_stderr;
char	*redirect;
char	*who;
unsigned int	refnum;
{
	int	i;

	if (process_list == null(Process **))
	{
		process_list = (Process **) new_malloc(sizeof(Process *));
		process_list_size = 1;
		process_list[0] = null(Process *);
	}
	for (i = 0; i < process_list_size; i++)
	{
		if (process_list[i] == null(Process *))
		{
			process_list[i] = (Process *)
				new_malloc(sizeof(Process));
			process_list[i]->name = null(char *);
			malloc_strcpy(&(process_list[i]->name), name);
			process_list[i]->logical = null(char *);
			malloc_strcpy(&(process_list[i]->logical), logical);
			process_list[i]->pid = pid;
			process_list[i]->p_stdin = p_stdin;
			process_list[i]->p_stdout = p_stdout;
			process_list[i]->p_stderr = p_stderr;
			process_list[i]->refnum = refnum;
			process_list[i]->redirect = null(char *);
			if (redirect)
				malloc_strcpy(&(process_list[i]->redirect),
					redirect);
			process_list[i]->server = curr_scr_win->server;
			process_list[i]->counter = 0;
			process_list[i]->exited = 0;
			process_list[i]->termsig = 0;
			process_list[i]->retcode = 0;
			process_list[i]->who = null(char *);
			process_list[i]->waitcmds = null(List *);
			if (who)
				malloc_strcpy(&(process_list[i]->who), who);
			return;
		}
	}
	process_list_size++;
	process_list = (Process **) new_realloc(process_list, sizeof(Process *)
		* process_list_size);
	process_list[process_list_size - 1] = null(Process *);
	process_list[i] = (Process *) new_malloc(sizeof(Process));
	process_list[i]->name = null(char *);
	malloc_strcpy(&(process_list[i]->name), name);
	process_list[i]->logical = null(char *);
	malloc_strcpy(&(process_list[i]->logical), logical);
	process_list[i]->pid = pid;
	process_list[i]->p_stdin = p_stdin;
	process_list[i]->p_stdout = p_stdout;
	process_list[i]->p_stderr = p_stderr;
	process_list[i]->refnum = refnum;
	process_list[i]->redirect = null(char *);
	if (redirect)
		malloc_strcpy(&(process_list[i]->redirect), redirect);
	process_list[i]->server = curr_scr_win->server;
	process_list[i]->counter = 0;
	process_list[i]->exited = 0;
	process_list[i]->termsig = 0;
	process_list[i]->retcode = 0;
	process_list[i]->who = null(char *);
	process_list[i]->waitcmds = null(List *);
	if (who)
		malloc_strcpy(&(process_list[i]->who), who);
}

/*
* kill_process: sends the given signal to the process specified by the given
* index into the process table.  After the signal is sent, the process is
* deleted from the process table 
*/
static	void	kill_process(index, signal)
	int	index,
	signal;
{
	if (process_list && (index < process_list_size) && process_list[index])
	{
		say("Sending signal %s (%d) to process %d: %s",
		    signals[signal], signal, index, process_list[index]->name);
#ifdef mips
		kill(getpgrp(process_list[index]->pid), signal);
#else
#if defined(ISC22) || defined(MUNIX) || defined(SVR4) || defined (POSIX)
		kill(-getpgrp(process_list[index]->pid), signal);
#else
		killpg(getpgrp(process_list[index]->pid), signal);
#endif /* defined(ISC22) || defined(MUNIX) || defined(SVR4) */
#endif /* mips */
		kill(process_list[index]->pid, signal);
	}
	else
		say("There is no process %d", index);
}

static	int	is_logical_unique(logical)
	char	*logical;
{
	int	i;

	if (logical)
	{
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i] && process_list[i]->logical &&
			    (stricmp(process_list[i]->logical, logical) == 0))
				return (0);
		}
	}
	return (1);
}

/*
* start_process: Attempts to start the given process using the SHELL as set
* by the user. 
*/
static	void	start_process(name, logical, redirect, who, refnum)
char	*name,
	*logical,
	*who,
	*redirect;
unsigned int	refnum;
{
	int	p0[2],
		p1[2],
		p2[2],
		pid,
		cnt;
	char	*shell,
		*flag,
		*arg;

	p0[0] = p0[1] = -1;
	p1[0] = p1[1] = -1;
	p2[0] = p2[1] = -1;
	if (pipe(p0) || pipe(p1) || pipe(p2))
	{
		say("Unable to start new process: %s", sys_errlist[errno]);
		if (p0[0] != -1)
		{
			close(p0[0]);
			close(p0[1]);
		}
		if (p1[0] != -1)
		{
			close(p1[0]);
			close(p1[1]);
		}
		if (p2[0] != -1)
		{
			close(p2[0]);
			close(p2[1]);
		}
		return;
	}
	switch (pid = fork())
	{
	case -1:
		say("Couldn't start new process!");
		break;
	case 0:
		setpgrp(0, getpid());
		signal(SIGINT, SIG_IGN);
		dup2(p0[0], 0);
		dup2(p1[1], 1);
		dup2(p2[1], 2);
		close(p0[1]);
		close(p1[0]);
		close(p2[0]);

		/* fix environment */
		for (cnt = 0, arg = environ[0]; arg; arg = environ[++cnt])
		{
			if (strncmp(arg, "TERM=", 5) == 0)
			{
				environ[cnt] = "TERM=tty";
				break;
			}
		}
		if ((shell = get_string_var(SHELL_VAR)) == null(char *))
		{
			char	**args; /* = null(char **) */
			int	max;

			cnt = 0;
			max = 5;
			args = (char **) new_malloc(sizeof(char *) * max);
			while (arg = next_arg(name, &name))
			{
				if (cnt == max)
				{
					max += 5;
					args = (char **) new_realloc(args,
						sizeof(char *) * max);
				}
				args[cnt++] = arg;
			}
			args[cnt] = null(char *);
			setuid(getuid()); /* If we are setuid, set it back! */
			setgid(getgid());
			execvp(args[0], args);
		}
		else
		{
			if ((flag = get_string_var(SHELL_FLAGS_VAR)) ==
					null(char *))
				flag = empty_string;
			setuid(getuid());
			setgid(getgid());
			execl(shell, shell, flag, name, null(char *));
		}
		sprintf(buffer, "*** Error starting shell \"%s\": %s\n", shell,
			sys_errlist[errno]);
		write(1, buffer, strlen(buffer));
		_exit(0);
		break;
	default:
		close(p0[0]);
		close(p1[1]);
		close(p2[1]);
		add_process(name, logical, pid, p0[1], p1[0], p2[0], redirect,
			who, refnum);
		break;
	}
}

/*
* text_to_process: sends the given text to the given process.  If the given
* process index is not valid, an error is reported and 1 is returned.
* Otherwise 0 is returned. 
* Added show, to remove some bad recursion, phone, april 1993
*/
int	text_to_process(index, text, show)
int	index;
char	*text;
int	show;
{
	int	ref;

	if (valid_process_index(index) == 0)
	{
		say("No such process number %d", index);
		return (1);
	}
	ref = process_list[index]->refnum;
	message_to(ref);
	if (show)
		put_it("%s%s", get_prompt_by_refnum(ref), text); /* lynx */
	write(process_list[index]->p_stdin, text, strlen(text));
	write(process_list[index]->p_stdin, "\n", 1);
	set_prompt_by_refnum(ref, empty_string);
	/*  update_input(UPDATE_ALL); */
	message_to(0);
	return (0);
}

/*
* is_process_running: Given an index, this returns true if the index referes
* to a currently running process, 0 otherwise 
*/
int	is_process_running(index)
	int	index;
{
	if (process_list && process_list[index])
		return (!process_list[index]->exited);
	return (0);
}

/*
* lofical_to_index: converts a logical process name to it's approriate index
* in the process list, or -1 if not found 
*/
int	logical_to_index(logical)
	char	*logical;
{
	int	i;

	for (i = 0; i < process_list_size; i++)
	{
		if (process_list[i] && process_list[i]->logical &&
		    (stricmp(process_list[i]->logical, logical) == 0))
			return (i);
	}
	return (-1);
}

/*
* get_process_index: parses out a process index or logical name from the
* given string 
*/
int	get_process_index(args)
	char	**args;
{
	char	*index;

	if (index = next_arg(*args, args))
	{
		if (*index == '%')
			index++;
		else
			return (-1);
		if (is_number(index))
			return (atoi(index));
		else
			return (logical_to_index(index));
	}
	else
		return (-1);
}

/* is_process: checks to see if arg is a valid process specification */
int	is_process(arg)
char	*arg;
{
	if (arg && *arg == '%')
	{
		arg++;
		if (is_number(arg) || (logical_to_index(arg) != -1))
			return (1);
	}
	return (0);
}

/*
 * exec: the /EXEC command.  Handles the whole IRCII process crap. 
 */
/*ARGSUSED*/
void	execcmd(command, args)
char	*command,
	*args;
{
	char	*who = null(char *),
		*logical = null(char *),
		*redirect, /* = null(char *), */
		*flag;
	unsigned int	refnum = 0;
	int	sig,
		len,
		i,
		refnum_flag = 0,
		logical_flag = 0;

	if (get_int_var(EXEC_PROTECTION_VAR) && (send_text_flag != -1))
	{
		echo(null(char *),"*** WARNING:  You are attempting to use \
EXEC from within an ON function!");
		sprintf(buffer,"*** Command \"%s\" not executed!",args);
		echo(null(char *),buffer);
		echo(null(char *),"*** Please read the /HELP SET \
EXEC_PROTECTION documentation");
		echo(null(char *),"*** for important details about this!");
		return;
	}
#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
	{
		say("You are not permitted to use EXEC.");
		return;
	}
#endif /* DAEMON_UID */
	if (*args == null(char))
	{
		list_processes();
		return;
	}
	redirect = null(char *);
	while ((*args == '-') && (flag = next_arg(args, &args)))
	{
		if (*flag == '-')
		{
			len = strlen(++flag);
			if (strnicmp(flag, "OUT", len) == 0)
			{
				redirect = "PRIVMSG";
				if ((who = get_channel_by_refnum(0)) ==
						null(char *))
				{
					say("No current channel in this window for -OUT");
					return;
				}
			}
			else if (strnicmp(flag, "NAME", len) == 0)
			{
				logical_flag = 1;
				if ((logical = next_arg(args, &args)) ==
						null(char *))
				{
					say("You must specify a logical name");
					return;
				}
			}
			else if (strnicmp(flag, "WINDOW", len) == 0)
			{
				refnum_flag = 1;
				refnum = current_refnum();
			}
			else if (strnicmp(flag, "MSG", len) == 0)
			{
				if (doing_privmsg)
					redirect = "NOTICE";
				else
					redirect = "PRIVMSG";
				if ((who = next_arg(args, &args)) ==
						null(char *))
				{
					say("No nicknames specified");
					return;
				}
			}
			else if (strnicmp(flag, "CLOSE", len) == 0)
			{
				if ((i = get_process_index(&args)) == -1)
				{
					say("Missing process number or logical name.");
					return;
				}
				if (is_process_running(i))
				{
					process_list[i]->p_stdin =
					  exec_close(process_list[i]->p_stdin);
					process_list[i]->p_stdout =
					  exec_close(process_list[i]->p_stdout);
					process_list[i]->p_stderr =
					  exec_close(process_list[i]->p_stderr);
				}
				else
					say("No such process running!");
				return;
			}
			else if (strnicmp(flag, "NOTICE", len) == 0)
			{
				redirect = "NOTICE";
				if ((who = next_arg(args, &args)) ==
						null(char *))
				{
					say("No nicknames specified");
					return;
				}
			}
			else if (strnicmp(flag, "IN", len) == 0)
			{
				if ((i = get_process_index(&args)) == -1)
				{
					say("Missing process number or \
logical name.");
					return;
				}
				text_to_process(i, args, 1);
				return;
			}
			else
			{
				if ((i = get_process_index(&args)) == -1)
				{
					say("Invalid process specification");
					return;
				}
				if ((sig = atoi(flag)) > 0)
				{
					if ((sig > 0) && (sig < NSIG))
						kill_process(i, sig);
					else
						say("Signal number can be \
from 1 to %d", NSIG);
					return;
				}
				for (sig = 1; sig < NSIG; sig++)
				{
					if (!strnicmp(signals[sig], flag, len))
					{
						kill_process(i, sig);
						return;
					}
				}
				say("No such signal: %s", flag);
				return;
			}
		}
		else
			break;
	}
	if (is_process(args))
	{
		if ((i = get_process_index(&args)) == -1)
		{
			say("Invalid process specification");
			return;
		}
		if (valid_process_index(i))
		{
			message_to(refnum);
			if (refnum_flag)
			{
				process_list[i]->refnum = refnum;
				if (refnum)
					say("Output from process %d (%s) \
now going to this window", i, process_list[i]->name);
				else
					say("Output from process %d (%s) \
not going to any window", i, process_list[i]->name);
			}
			malloc_strcpy(&(process_list[i]->redirect), redirect);
			malloc_strcpy(&(process_list[i]->who), who);
			if (redirect)
			{
				say("Output from process %d (%s) now going \
to %s", i, process_list[i]->name, who);
			}
			else
				say("Output from process %d (%s) now going \
to you", i, process_list[i]->name);
			if (logical_flag)
			{
				if (logical)
				{
					if (is_logical_unique(logical))
					{
						malloc_strcpy(&(
						    process_list[i]->logical),
						    logical);
						say("Process %d (%s) is now c\
alled %s", i, process_list[i]->name, process_list[i]->logical);
					}
					else
						say("The name %s is not unique!"
							, logical);
				}
				else
					say("The name for process %d (%s) has \
been removed", i, process_list[i]->name);
			}
			message_to(0);
		}
		else
			say("Invalid process specification");
	}
	else
	{
		if (is_logical_unique(logical))
			start_process(args, logical, redirect, who, refnum);
		else
			say("The name %s is not unique!", logical);
	}
}

/*
 * clean_up_processes: kills all processes in the procss list by first
 * sending a SIGTERM, then a SIGKILL to clean things up 
 */
void	clean_up_processes()
{
	int	i;

	if (process_list_size)
	{
		say("Cleaning up left over processes....");
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i])
				kill_process(i, SIGTERM);
		}
		sleep(2);	/* Give them a chance for a graceful exit */
		for (i = 0; i < process_list_size; i++)
		{
			if (process_list[i])
				kill_process(i, SIGKILL);
		}
	}
}

/*
 * close_all_exec:  called when we fork of a wserv process for interaction
 * with screen/X, to close all unnessicary fd's that might cause problems
 * later.
 */
void	close_all_exec()
{
	int	i;
	int	tmp;

	tmp = window_display;
	window_display = 0;
	for (i = 0; i < process_list_size; i++)
		if (process_list[i])
		{
			if (process_list[i]->p_stdin)
				new_close(process_list[i]->p_stdin);
			if (process_list[i]->p_stdout)
				new_close(process_list[i]->p_stdout);
			if (process_list[i]->p_stderr)
				new_close(process_list[i]->p_stderr);
			delete_process(i);
			kill_process(i, SIGKILL);
		}
	window_display = tmp;
}

void	exec_server_delete(i)
int	i;
{
	int	j;

	for (j = 0; j < process_list_size; j++)
		if (process_list[j] && process_list[j]->server >= i)
			process_list[j]->server--;
}
