/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#define STREAM_LIB

#include	<errno.h>
#include	<sys/types.h>
#include	<fcntl.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<limits.h>
#include	"memory_debug.h"
#include	"task.h"
#include	"utils.h"
#include	"lock_level.h"
#include	"pri_level.h"

extern S_TABLE s_file_table;
extern SEM stream_lock;


char *
setup_ret_str_child(char * msg,char * child)
{
int len;
char * p, * q;
	if ( child == 0 || *child == 0 )
		return msg;
	len = 0;
	for ( p = child ; *p ; p ++ )
		if ( *p < 0x21 || *p == ']' || *p == '\\' )
			len += 4;
		else	len ++;
	msg = d_re_alloc(msg,strlen(msg)+len+3);
	q = &msg[strlen(msg)];
	p = child;
	*q++ = '[';
	for ( ; *p ; p ++ )
		if ( *p < 0x21 || *p == ']' || *p == '\\' ) {
			q[0] = '\\';
			q[1] = (((*p)>>6)&0x7)+'0';
			q[2] = (((*p)>>3)&0x7)+'0';
			q[3] = ((*p)&0x7)+'0';
			q += 4;
		}
		else {
			*q++ = *p;
		}
	*q++ = ']';
	*q = 0;
	return msg;		
}

int
launch_proc(
	char * str,
	char ** argv,
	STREAM ** stpp,
	char * close_flags,
	char * ret_str_parent,
	char * ret_str_child)
{
char ** _argv;
int id;
int i,j,target;
char *fp,* org;
int len;
int p[2];
char ch;
int slen;
int er;
int * stpfd, * _stpfd;
int * pipe_dup;
int pd;
PROC_SEND ps;
char * msg;
char * _close_flags, * cf;
int ret_errno;

	lock_task(stream_lock);
	for ( len = 0 ; stpp[len] ; len ++ );
	stpfd = d_alloc((len*2+1)*sizeof(int),122);
	_close_flags = d_alloc(len*2+1,235);
	msg = copy_str("+");
	target = 0;
	for ( i = 0 ; i < len ; i ++ ) {
		if ( (*stpp[i]->h.tbl->proc_send)(stpp[i],&ps) < 0 )
			er_panic("cannot send stream");
		msg = d_re_alloc(msg,strlen(msg)+strlen(ps.msg)+1);
		strcpy(&msg[strlen(msg)],ps.msg);
		if ( close_flags[i] ) {
			for ( j = 0 ; j < 2 ; j ++ ) {
				if ( ps.fid[j] < 0 )
					break;
				_close_flags[target] = 1;
				stpfd[target++] = dup(ps.fid[j]);
			}
		}
		else {
			for ( j = 0 ; j < 2 ; j ++ ) {
				if ( ps.fid[j] < 0 )
					break;
				_close_flags[target] = 0;
				stpfd[target++] = ps.fid[j];
			}
		}
	}
	unlock_task(stream_lock,"launch_proc");
	for ( i = 0 ; i < len ; i ++ ) {
		if ( close_flags[i] )
			s_close(stpp[i]);
	}

	len = target;

	if ( strlen(msg) == 1 ) {
		d_f_ree(msg);
		msg = 0;
		_argv = 0;
	}
	else {
	int a_len;
		msg = setup_ret_str_child(msg,ret_str_child);
		for ( i = 0 ; argv[i] ; i ++ );
		a_len = i;
		_argv = d_alloc(sizeof(char*)*(a_len+1),123);
		_argv[0] = argv[0];
		_argv[1] = msg;
		for ( i = 1 ; argv[i] ; i ++ )
			_argv[i+1] = argv[i];
		_argv[i+1] = 0;
		argv = _argv;
	}

	fp = d_alloc(OPEN_MAX+1,130);
	org = d_alloc(len,131);
	pipe_dup = d_alloc(sizeof(int)*OPEN_MAX,131);

	er = pipe(p);
retry:
	errno = 0;
	id = fork();
	if ( id == 0 ) {
		/* child process */

		close(p[1]);
		if ( p[0] <= 3+len ) {
			pd = 0;
			for ( ; ; ) {
				pipe_dup[pd] = dup(p[0]);
				if ( pipe_dup[pd] <= 3 )
					er_panic("launch_proc");
				if ( pipe_dup[pd] > 3+len )
					break;
				pd ++;
			}
			for ( i = 0 ; i < pd ; i ++ )
				close(pipe_dup[i]);
			close(p[0]);
			p[0] = pipe_dup[pd];
		}
		for ( i = 0 ; i < len ; i ++ )
			org[i] = 0;
		for ( i = 0 ; i < OPEN_MAX+1 ; i ++ ){
			if ( i == p[0] )
				fp[i] = 1;
			else	fp[i] = 0;
		}
		for ( i = 0 ; i < len ; i ++ ) {
			if ( stpfd[i] < 0  )
				org[i] = 1;
			else if ( stpfd[i] < len + 3 ) {
				fp[stpfd[i]] = 1;
				org[i] = 1;
			}
		}
		j = 0;
		for ( i = 3 ; i < 3 + len ; i ++ ) {
			if ( fp[i] )
				continue;
			for ( ; j < len && org[j] ; j ++ );
			if ( j >= len )
				break;
			close(i);
			dup(stpfd[j]);
			close(stpfd[j]);
			j ++;
		}
		for ( ; i <= OPEN_MAX ; i ++ ) {
			if ( p[0] == i )
				continue;
			close(i);
		}
		if ( read(p[0],&ch,1) < 0 ) {
			printf("pp p %i\n",p[0]);
			perror("launch");
		}
		close(p[0]);
/*
		if ( ret_str_child ) {
			slen = strlen(ret_str_child);
			for ( i = 3 ; i < 3 + len ; i ++ )
				write(i,ret_str_child,slen);
		}
*/
		pthread_detach(_get_tid());
		er = execvp(str,argv);
		exit(1);
	}
	if ( id < 0 && errno == EAGAIN ) {
		sleep_sec(1);
		goto retry;
	}
	ret_errno = errno;
	close(p[0]);

	if ( ret_str_parent )
		slen = strlen(ret_str_parent);
	cf = _close_flags;
	for ( i = 0 , _stpfd = stpfd ; i < len ; _stpfd ++ , cf ++ , i ++ ) {
		if ( ret_str_parent )
			write(*_stpfd,ret_str_parent,slen);
		if ( *cf )
			close(*_stpfd);
	}

	if ( id == -1 ) {
		close(p[1]);
		d_f_ree(fp);
		d_f_ree(org);
		d_f_ree(pipe_dup);
		d_f_ree(stpfd);
		if ( _argv ) {
			d_f_ree(_argv);
			d_f_ree(msg);
		}
		return - ret_errno;
	}
	errno = 0;
	er = write(p[1],&ch,1);
	if ( errno )
		printf("write %i\n",errno);
	close(p[1]);
	d_f_ree(fp);
	d_f_ree(org);
	d_f_ree(pipe_dup);
	d_f_ree(stpfd);
	d_f_ree(_close_flags);
	if ( _argv ) {
		d_f_ree(_argv);
		d_f_ree(msg);
	}
	return id;
}
