///////////////////////////////////////////////////////////////////////////////
// File   :  ksh.c
// Date   :  October 2017
// Author :  Alain Greiner
///////////////////////////////////////////////////////////////////////////////
// This application implements a minimal shell for ALMOS-MKH.
//
// This user KSH process contains two POSIX threads:
// - the "main" thread contains the infinite loop implementing
//   the children processes termination monitoring using the wait syscall.
// - the "interactive" thread contains the infinite loop implementing
//   the command interpreter attached to the TXT terminal.
//   This "interactive" thread block and deschedules when the KSH process
//   loses the TXT terminal ownership. It is reactivated when the KSH
//   process returns in background.
//
// Note: the children processes are created by the <load> command, and are
// attached to the same TXT terminal as the KSH process itself.
// . A child process can be lauched in foreground: the KSH process loses
//   the TXT terminal ownership, that is transfered to the new process.
// . A child process can be lauched in background: the KSH process keeps
//   the TXT terminal ownership. that is transfered to the new process.
///////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <almosmkh.h>
#include <semaphore.h>

#define CMD_MAX_SIZE   (256)    // max number of characters in one command
#define LOG_DEPTH      (32)     // max number of registered commands 
#define MAX_ARGS	   (32)     // max number of arguments in a command
#define FIFO_SIZE      (1024)   // FIFO depth for recursive ls

#define KSH_DEBUG      0

////////////////////////////////////////////////////////////////////////////////
//	   Structures
////////////////////////////////////////////////////////////////////////////////

// one entry in the registered commands array
typedef struct log_entry_s 
{
	char          buf[CMD_MAX_SIZE];
	unsigned int  count;
}
log_entry_t;

// one entry in the supported command types array
typedef struct ksh_cmd_s 
{
	char * name;
	char * desc;
	void   (*fn)( int , char ** );
}
ksh_cmd_t;


////////////////////////////////////////////////////////////////////////////////
//	   Global Variables
////////////////////////////////////////////////////////////////////////////////

ksh_cmd_t       cmd[];                    // array of supported commands

log_entry_t     log_entries[LOG_DEPTH];   // array of registered commands

unsigned int    ptw;                      // write pointer in log_entries[]
unsigned int    ptr;                      // read pointer in log_entries[]

pthread_attr_t  attr;                     // interactive thread attributes

sem_t           semaphore;                // block interactive thread when zero

////////////////////////////////////////////////////////////////////////////////
//	   Shell  Commands
////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////
static void cmd_cat( int argc , char **argv )
{
	char         * path;

	if (argc != 2) 
    {
		printf("  usage: cat pathname\n");
		return;
	}

	path = argv[1];

    printf("  error: not implemented yet\n");

/*
	// open the file
	fd = open( path , O_RDONLY , 0 );
	if (fd < 0) 
    {
		printf("  error: cannot open %s\n", path);
		goto exit;
	}

	// get file size 
	if (stat(path, &st) == -1)
    {
		printf("  error: cannot stat %s\n", path);
		goto exit;
	}
	if (S_ISDIR(st.st_mode)) {
		printf("  error: %s is a directory\n", path);
		goto exit;
	}
	size = st.st_size;

	// mmap the file 
	buf = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
	if (buf == NULL || buf == (char *)-1) {
		printf("  error: cannot map %s\n", path);
		goto exit;
	}

	// set terminating '0'
	buf[size-1] = 0;

	// display the file content
	printf("%s", buf);

exit:
	if (buf != NULL) munmap(buf, size);
	if (fd >= 0) close(fd);
*/

    // release semaphore to get next command
    sem_post( &semaphore );

}   // end cmd_cat()

////////////////////////////////////////////
static void cmd_cd( int argc , char **argv )
{
	char * path;

	if (argc != 2)
    {
		printf("  usage: cd pathname\n");
		return;
	}

	path = argv[1];

    printf("  error: not implemented yet\n");

    // release semaphore to get next command
    sem_post( &semaphore );

}   // end cmd_cd()

/////////////////////////////////////////
static void cmd_cp(int argc, char **argv)
{
//	int src_fd = -1, dst_fd = -1;
//	char *srcpath, *dstpath;
//	struct stat st;
//	size_t size, i;
//	char buf[1024];

	if (argc != 3) 
    {
		printf("  usage: cp src_pathname dst_pathname\n");
		return;
	}

    printf("  error: not implemented yet\n");

/*
	srcpath = argv[1];
	dstpath = argv[2];

	// open the src file 
	src_fd = open(srcpath, O_RDONLY, 0);
	if (src_fd < 0) {
		printf("  error: cannot open %s / err = %d\n", srcpath, errno);
		goto exit;
	}

	// get file size 
	if (stat(srcpath, &st) == -1) {
		printf("  error: cannot stat %s\n", srcpath);
		goto exit;
	}
	if (S_ISDIR(st.st_mode)) {
		printf("  error: %s is a directory\n", srcpath);
		goto exit;
	}
	size = st.st_size;

	// open the dst file 
	dst_fd = open(dstpath, O_CREAT|O_TRUNC|O_RDWR, 0);
	if (dst_fd < 0) {
		printf("  error: cannot open %s / err = %d\n", dstpath, errno);
		goto exit;
	}
	if (stat(dstpath, &st) == -1) {
		printf("  error: cannot stat %s\n", dstpath);
		goto exit;
	}
	if (S_ISDIR(st.st_mode)) {
		printf("  error: %s is a directory\n", dstpath);
		goto exit;
	}

	i = 0;
	while (i < size)
	{
		size_t rlen = (size - i < 1024 ? size - i : 1024);
		size_t wlen;
		ssize_t ret;

		// read the source 
		ret = read(src_fd, buf, rlen);
		if (ret == -1) {
			printf("  error: cannot read from file %s\n", srcpath);
			goto exit;
		}
		rlen = (size_t)ret;

		// write to the destination 
		ret = write(dst_fd, buf, rlen);
		if (ret == -1) {
			printf("  error: cannot write to file %s\n", dstpath);
			goto exit;
		}
		wlen = (size_t)ret;

		// check 
		if (wlen != rlen) {
			printf("  error: cannot write on device\n");
			goto exit;
		}

		i += rlen;
	}

exit:
	if (src_fd >= 0) close(src_fd);
	if (dst_fd >= 0) close(dst_fd);
*/

    // release semaphore to get next command
    sem_post( &semaphore );

}   // end cmd_cp()

/////////////////////////////////////////////////
static void cmd_display( int argc , char **argv )
{
    unsigned int  cxy;
    unsigned int  lid;
    unsigned int  pid;
    unsigned int  txt_id;

    if( strcmp( argv[1] , "vmm" ) == 0 )
    {
        if( argc != 4 )
        {
		    printf("  usage: display vmm cxy pid\n");
		    return;
	    }

	    cxy = atoi(argv[2]);
	    pid = atoi(argv[3]);

        if( display_vmm( cxy , pid ) )
        {
            printf("  error: no process %x in cluster %x\n", pid , cxy );
        }
    }
    else if( strcmp( argv[1] , "sched" ) == 0 )
    {
        if( argc != 4 )
        {
		    printf("  usage: display sched cxy lid\n");
		    return;
	    }

	    cxy = atoi(argv[2]);
	    lid = atoi(argv[3]);

        if( display_sched( cxy , lid ) )
        {
            printf("  error: illegal arguments cxy = %x / lid = %d\n", cxy, lid );
        }
    }
    else if( strcmp( argv[1] , "process" ) == 0 )
    {
        if( argc != 3 )
        {
		    printf("  usage: display process cxy\n");
		    return;
	    }

	    cxy = atoi(argv[2]);

        if( display_cluster_processes( cxy ) )
        {
            printf("  error: illegal argument cxy = %x\n", cxy );
        }
    }
    else if( strcmp( argv[1] , "txt" ) == 0 )
    {
        if( argc != 3 )
        {
		    printf("  usage: display txt txt_id\n");
		    return;
	    }

	    txt_id = atoi(argv[2]);

        if( display_txt_processes( txt_id ) )
        {
            printf("  error: illegal argument txt_id = %x\n", txt_id );
        }
    }
    else if( strcmp( argv[1] , "vfs" ) == 0 )
    {
        if( argc != 2 )
        {
		    printf("  usage: display vfs\n");
		    return;
	    }

        display_vfs();
    }
    else if( strcmp( argv[1] , "chdev" ) == 0 )
    {
        if( argc != 2 )
        {
		    printf("  usage: display chdev\n");
		    return;
	    }

        display_chdev();
    }
    else if( strcmp( argv[1] , "dqdt" ) == 0 )
    {
        if( argc != 2 )
        {
		    printf("  usage: display dqdt\n");
		    return;
	    }

        display_dqdt();
    }
    else
    {
        printf("  usage: display (vmm/sched/process/vfs/chdev/txt) [arg2] [arg3]\n");
    }

    // release semaphore to get next command
    sem_post( &semaphore );

} // end cmd_display()

/////////////////////////////////////////
static void cmd_fg(int argc, char **argv)
{
	unsigned int pid;

	if (argc != 2) 
    {
		printf("  usage: %s pid\n", argv[0]);
		return;
	}

    pid = atoi( argv[1] );   

    if( pid == 0 )
    {
		printf("  error: PID cannot be 0\n" );
	}

    if( fg( pid ) )
    {
		printf("  error: cannot find process %x\n", pid );
	}

    // release semaphore to get next command
    sem_post( &semaphore );

}  // end cmd_fg()

//////////////////////////////////////////////
static void cmd_help( int argc , char **argv )
{
	unsigned int i;

	if (argc != 1) 
    {
		printf("  usage: %s\n", argv[0]);
		return;
	}

	printf("available commands:\n");
	for (i = 0 ; cmd[i].name ; i++) 
    {
		printf("\t%s\t : %s\n", cmd[i].name , cmd[i].desc);
	}

    // release semaphore to get next command
    sem_post( &semaphore );

}   // end cmd_help()

//////////////////////////////////////////////
static void cmd_kill( int argc , char **argv )
{
	unsigned int pid;

	if (argc != 2) 
    {
		printf("  usage: %s pid\n", argv[0]);
		return;
	}

	pid = atoi( argv[1] );

    if( pid == 0 )
    {
		printf("  error: kernel process 0 cannot be killed\n" );
	}

	if( kill( pid , SIGKILL ) )
    {
		printf("  error: process %x cannot be killed\n", pid );
	}

    // release semaphore to get next command
    sem_post( &semaphore );

}   // end cmd_kill()

//////////////////////////////////////////////
static void cmd_load( int argc , char **argv )
{
	int                  ret_fork;           // return value from fork 
	int                  ret_exec;           // return value from exec
    unsigned int         ksh_pid;            // KSH process PID
	char               * pathname;           // path to .elf file
    unsigned int         background;         // background execution if non zero

	if( (argc < 2) || (argc > 3) )  
    {
		printf("  usage: %s pathname [&]\n", argv[0] );
		return;
	}

	pathname = argv[1];

    if( argc == 3 ) background = (argv[2][0] == '&');
    else            background = 0;

    // get KSH process PID
    ksh_pid = getpid();

#if KSH_DEBUG
printf("\n[KSH_DEBUG] in %s : ksh PID %x / path %s / background %d\n",
__FUNCTION__, ksh_pid, argv[1], background );
#endif

    // KSH process fork CHILD process
	ret_fork = fork();

    if ( ret_fork < 0 )     // it is a failure reported to KSH
    {
        printf("  error: ksh process unable to fork\n");
        return;
    }
    else if (ret_fork == 0) // it is the CHILD process 
    {
        // CHILD process exec NEW process
        ret_exec = execve( pathname , NULL , NULL );

        // this is only executed in case of exec failure
        if( ret_exec )
        {
            printf("  error: child process unable to exec <%s>\n", pathname );
            exit( 0 );
        }   
	} 
    else                    // it is the KSH process : ret_fork is the new process PID
    {

#if KSH_DEBUG
int sem_value;
sem_getvalue( &semaphore , &sem_value );
printf("\n[KSH_DEBUG] in %s : child PID %x / background %d / sem_value %d\n",
ret_fork, background, sem_value  );
#endif

        if( background )        // child in background =>  KSH keeps TXT ownership
        {
            // execve() tranfered TXT ownership to child => give it back to KSH
            fg( ksh_pid );

            // release semaphore to get next command
            sem_post( &semaphore );
        }
    }
}   // end cmd_load

/////////////////////////////////////////////
static void cmd_log( int argc , char **argv )
{
	unsigned int i;

	printf("--- registered commands ---\n");
	for (i = 0; i < LOG_DEPTH; i++) 
    {
		printf(" - %d\t: %s\n", i, &log_entries[i].buf);
	}

    // release semaphore to get next command
    sem_post( &semaphore );

} // end cmd_log()


////////////////////////////////////////////
static void cmd_ls( int argc , char **argv )
{
	char  * path;

//  struct dirent * file;
//  DIR *dir;

	if (argc == 1)
    {
		path = ".";
	}
    else if (argc == 2) 
    {
		path = argv[1];
	} 
    else 
    {
		printf("  usage: ls [path]\n");
		return;
	}

    printf("  error: not implemented yet\n");
/*
	dir = opendir( path );
	while ((file = readdir(dir)) != NULL)
	{
		printf(" %s\n", file->d_name);
	}
	closedir(dir);
*/

    // release semaphore to get next command
    sem_post( &semaphore );

} // end cmd_ls()

///////////////////////////////////////////////
static void cmd_mkdir( int argc , char **argv )
{
	char * pathname;

	if (argc != 2)
    {
		printf("  usage: mkdir pathname\n");
		return;
	}

    pathname = argv[1];

    printf("  error: not implemented yet\n");

    // release semaphore to get next command
    sem_post( &semaphore );

} // end cmd_mkdir()

////////////////////////////////////////////
static void cmd_mv( int argc , char **argv )
{

	if (argc < 3)
	{
		printf("  usage : %s src_pathname dst_pathname\n", argv[0]);
		return;
	}

    printf("  error: not implemented yet\n");
    
    // release semaphore to get next command
    sem_post( &semaphore );

}  // end cmd_mv

/////////////////////////////////////////////
static void cmd_pwd( int argc , char **argv )
{
	char buf[1024];

	if (argc != 1)
    {
		printf("  usage: pwd\n");
		return;
	}

	if ( getcwd( buf , 1024 ) ) 
    {
		printf("  error: unable to get current directory\n");
	}
    else 
    {
		printf("%s\n", buf);
	}

    // release semaphore to get next command
    sem_post( &semaphore );

}  // end cmd_pwd()

////////////////////////////////////////////
static void cmd_rm( int argc , char **argv )
{
	char * pathname;

	if (argc != 2)
    {
		printf("  usage: rm pathname\n");
		return;
	}

	pathname = argv[1];

    printf("  error: not implemented yet\n");

    // release semaphore to get next command
    sem_post( &semaphore );

}  // end_cmd_rm()

///////////////////////////////////////////////
static void cmd_rmdir( int argc , char **argv )
{
    // same as cmd_rm()
	cmd_rm(argc, argv);
}

///////////////////////////////////////////////
static void cmd_trace( int argc , char **argv )
{
    unsigned int cxy;
    unsigned int lid;

	if (argc != 3)
    {
		printf("  usage: trace cxy lid \n");
		return;
	}

    cxy = atoi(argv[1]);
    lid = atoi(argv[2]);

    if( trace( 1 , cxy , lid ) )
    {
        printf("  error: core[%x,%d] not found\n", cxy, lid );
    }

    // release semaphore to get next command
    sem_post( &semaphore );

}  // end cmd_trace

///////////////////////////////////////////////
static void cmd_untrace( int argc , char **argv )
{
    unsigned int cxy;
    unsigned int lid;

	if (argc != 3)
    {
		printf("  usage: untrace cxy lid \n");
		return;
	}

    cxy = atoi(argv[1]);
    lid = atoi(argv[2]);

    if( trace( 0 , cxy , lid ) )
    {
        printf("  error: core[%x,%d] not found\n", cxy, lid );
    }

    // release semaphore to get next command
    sem_post( &semaphore );

}  // end cmd_untrace()

///////////////////////////////////////////////////////////////////////////////////
// Array of commands
///////////////////////////////////////////////////////////////////////////////////

ksh_cmd_t cmd[] =
{
	{ "cat",     "display file content",                            cmd_cat     },
	{ "cd",      "change current directory",                        cmd_cd      },
	{ "cp",      "replicate a file in file system",                 cmd_cp      },
    { "fg",      "put a process in foreground",                     cmd_fg      },
    { "display", "display vmm/sched/process/vfs/chdev/txt",         cmd_display },
	{ "load",    "load an user application",                        cmd_load    },
	{ "help",    "list available commands",                         cmd_help    },
	{ "kill",    "kill a process (all threads)",                    cmd_kill    },
	{ "log",     "list registered commands",                        cmd_log     },
	{ "ls",      "list directory entries",                          cmd_ls      },
	{ "mkdir",   "create a new directory",                          cmd_mkdir   },
	{ "mv",      "move a file in file system",                      cmd_mv      },
	{ "pwd",     "print current working directory",                 cmd_pwd     },
	{ "rm",      "remove a file from file system",                  cmd_rm      },
	{ "rmdir",   "remove a directory from file system",             cmd_rmdir   },
	{ "trace",   "activate trace for a given core",                 cmd_trace   },
	{ "untrace", "desactivate trace for a given core",              cmd_untrace },
	{ NULL,      NULL,								                NULL        }
};

////////////////////////////////////////////////////////////////////////////////////
// This function analyses one command (with arguments), executes it, and returns.
////////////////////////////////////////////////////////////////////////////////////
static void parse( char * buf )
{
	int argc = 0;
	char *argv[MAX_ARGS];
	int i;
	int len = strlen(buf);

	// build argc/argv
	for (i = 0; i < len; i++) 
    {
		if (buf[i] == ' ') 
        {
			buf[i] = '\0';
		}
        else if (i == 0 || buf[i - 1] == '\0') 
        {
			if (argc < MAX_ARGS) 
            {
				argv[argc] = &buf[i];
				argc++;
			}
		}
	}

	if (argc > 0)
    {
		int found = 0;

		argv[argc] = NULL;

		// try to match typed command 
		for (i = 0; cmd[i].name; i++)
        {
			if (strcmp(argv[0], cmd[i].name) == 0)
            {
				cmd[i].fn(argc, argv);
				found = 1;
				break;
			}
		}

		if (!found)  // undefined command
        {
			printf("  error : undefined command <%s>\n", argv[0]);

            // release semaphore to get next command
            sem_post( &semaphore );
		}
	}
}  // end parse()

/////////////////////////
static void interactive()
{
	char         c;						  // read character
	char         buf[CMD_MAX_SIZE];		  // buffer for one command
    unsigned int end_command;             // last character found in a command
	unsigned int count;     			  // pointer in command buffer
	unsigned int i;						  // index for loops
	unsigned int state;                   // escape sequence state

char string[80];

	enum fsm_states 
    {
		NORMAL = 0,
		ESCAPE = 1,
		BRAKET = 2,
	};

	// This lexical analyser writes one command line in the command buffer.
	// It is implemented as a 3 states FSM to handle the following escape sequences:
	// - ESC [ A : up arrow
	// - ESC [ B : down arrow
	// - ESC [ C : right arrow
	// - ESC [ D : left arrow
	// The three states have the following semantic:
	// - NORMAL : no (ESC) character has been found
	// - ESCAPE : the character (ESC) has been found
	// - BRAKET : the wo characters (ESC,[) have been found

    // external loop on the commands
    // the in teractive thread should not exit this loop
	while (1)
	{
	    // initialize command buffer
	    memset( buf, 0x20 , sizeof(buf) );   // TODO useful ?
	    count = 0;
	    state = NORMAL;

        // block if the KSH process is not the TXT owner
        // - if the command is not a "load"
        //   the semaphore must be released by the cmd_***()
        // - if the command is a load, it depends on 
        //   the "background" argument
        sem_wait( &semaphore );

        // display prompt on a new line
        printf("\n[ksh] ");
 
        end_command = 0;

        // internal loop on characters in one command
        while( end_command == 0 )
        {
            // get one character from TXT_RX
   	        c = (char)getchar();

            if( c == 0 ) continue;

		    if( state == NORMAL )  // we are not in an escape sequence
		    {
				if ((c == '\b') || (c == 0x7F))  // backspace => remove one character
				{
				    if (count > 0)
                    {
				        printf("\b \b");
				        count--;
				    }
				}
				else if (c == '\n')	             // new line => end of command
				{
				    if (count > 0)               // analyse & execute command
				    {
					    // complete command with NUL character
					    buf[count] = 0;
                        count++;

				        // register command in log arrays
					    strcpy(log_entries[ptw].buf, buf);
					    log_entries[ptw].count = count;
					    ptw = (ptw + 1) % LOG_DEPTH;
					    ptr = ptw;

                        // echo character
                        putchar( c );

					    // call parser to analyse and execute command
					    parse( buf );
				    }
                    else                         // no command registered
                    {
                        // release semaphore to get next command
                        sem_post( &semaphore );
                    }

                    // exit internal loop on characters
                    end_command = 1;
                }
			    else if (c == '\t')	            // tabulation => do nothing
			   	{
			    }
			    else if (c == (char)0x1B)	    // ESC => start an escape sequence
			    {
                    state = ESCAPE;
			    }
			    else					       // normal character
				{
				    if (count < sizeof(buf) - 1)
				    {
                        // register character in command buffer
					    buf[count] = c;
					    count++;

                        // echo character
                        putchar( c );
					}
				}
			}
			else if( state == ESCAPE )  
			{
				if (c == '[')		//  valid sequence => continue
				{
					state = BRAKET;
				}
				else				   // invalid sequence => do nothing
				{
					state = NORMAL;
				}
			}
			else if( state == BRAKET )
			{
				if (c == 'D')   // valid  LEFT sequence => move buf pointer left
				{
					if (count > 0)
					{
						printf("\b");
						count--;
					}

					// get next user char
					state = NORMAL;
				}
				else if (c == 'C')   // valid  RIGHT sequence => move buf pointer right
				{
					if (count < sizeof(buf) - 1)
					{
						printf("%c", buf[count]);
						count++;
					}

					// get next user char
					state = NORMAL;
				}
				else if (c == 'A')   // valid  UP sequence => move log pointer backward
				{
					// cancel current command
					for (i = 0; i < count; i++) printf("\b \b");
					count = 0;

					// copy log command into buf
					ptr = (ptr - 1) % LOG_DEPTH;
					strcpy(buf, log_entries[ptr].buf);
					count = log_entries[ptr].count;

					// display log command
					printf("%s", buf);

					// get next user char
					state = NORMAL;
				}
				else if (c == 'B')   // valid  DOWN sequence => move log pointer forward
				{
					// cancel current command
					for (i = 0 ; i < count; i++) printf("\b \b");
					count = 0;

					// copy log command into buf
					ptr = (ptr + 1) % LOG_DEPTH;
					strcpy(buf, log_entries[ptr].buf);
					count = log_entries[ptr].count;

					// display log command
					printf("%s", buf);

					// get next user char
					state = NORMAL;
				}
				else				   // other character => do nothing
				{
					// get next user char
					state = NORMAL;
				}
			}
		}  // end internal while loop on characters
	}  // end external while loop on commands
}  // end interactive()

///////////////////////////////////
int main( int argc , char *argv[] )
{
    unsigned int cxy;             // owner cluster identifier for this KSH process
    unsigned int lid;             // core identifier for this KSH main thread
    int          status;          // child process termination status
    int          child_pid;       // child process identifier
    int          parent_pid;      // parent process identifier
    pthread_t    trdid;           // kernel allocated index for interactive thread
    unsigned int is_owner;        // non-zero if KSH process is TXT owner

    // initialize log buffer
	memset( &log_entries , 0, sizeof(log_entries));
	ptw   = 0;
	ptr   = 0;

    // get KSH process pid and core
    parent_pid = getpid();
    get_core( &cxy , & lid );

	printf( "\n\n~~~ KSH on core[%x,%d] ~~~\n\n", cxy , lid );

    // initializes the semaphore used to unblock the interactive thread
    sem_init( &semaphore , 0 , 1 );

    // initialize interactive thread attributes
    attr.attributes = PT_ATTR_DETACH | PT_ATTR_CLUSTER_DEFINED;
    attr.cxy        = cxy;

    // lauch the interactive thread 
    pthread_create( &trdid,
                    &attr,
                    &interactive,   // entry function
                    NULL ); 
    
    // enter infinite loop monitoring children processes termination 
    while( 1 )
    {
        // wait children termination
        child_pid = wait( &status );

#if KSH_DEBUG
if( WIFEXITED  (status) ) printf("\n[KSH] child process %x exited\n" , child_pid );
if( WIFSIGNALED(status) ) printf("\n[KSH] child process %x killed\n" , child_pid );
if( WIFSTOPPED (status) ) printf("\n[KSH] child process %x stopped\n", child_pid );
#endif

        // release semaphore if KSH process is TXT owner, to unblock interactive thread
        is_fg( parent_pid , &is_owner );
        if( is_owner ) sem_post( &semaphore );

    }
}  // end main()


