/////////////////////////////////////////////////////////////////////////////////////////
// File   :  ksh.c
// Date   :  October 2017
// Author :  Alain Greiner
/////////////////////////////////////////////////////////////////////////////////////////
// This application implements a minimal shell for ALMOS-MKH.
//
// This user 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, and handling one KSH command
//   per iteration. 
//
// The children processes are created by the <load> command, and are
// attached to the same TXT terminal as the parent KSH process.
// A child process can be launched in foreground or in background: 
// . when the child process is launched in foreground, the KSH process loses
//   the TXT terminal ownership, that is transfered to the child process.
// . when the child process is launched in background, the KSH process keeps
//   the TXT terminal ownership.
//
// We use a semaphore to synchronize the two KSH threads. After each command
// completion, the interactive thread check the TXT ownership (with a sem_wait),
// and blocks, if the KSH process loosed the TXT ownership (after a load,
// or for any other cause). It is unblocked with the following policy:
// . if the command is "not a load", the semaphore is incremented by the 
//   cmd_***() function when the command is completed, to get the next command 
//   in the while loop.    
// . if the command is a "load without &", the TXT is given to the NEW process by the
//   execve() syscall, and is released to the KSH process when NEW process terminates.
//   The KSH process is notified and the KSH main() function increments the semahore 
//   to allow the KSH interactive() function to handle commands.
// . if the command is a "load with &", the cmd_load() function returns the TXT 
//   to the KSH process and increment the semaphore, when the parent KSH process 
//   returns from the fork() syscall.
/////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <dirent.h>
#include <almosmkh.h>
#include <semaphore.h>
#include <hal_macros.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <shared_syscalls.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 PATH_MAX_SIZE      (256)    // max number of characters in a pathname 

#define DEBUG_MAIN          0
#define DEBUG_INTER         0
#define DEBUG_EXECUTE       0
#define DEBUG_CMD_CAT       0
#define DEBUG_CMD_CP        0
#define DEBUG_CMD_LOAD      0
#define DEBUG_CMD_LS        0
#define DEBUG_CMD_PS        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       command[];                  // 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

pthread_t       trdid;                      // interactive thread identifier

char            pathname[PATH_MAX_SIZE];    // pathname for a file

char            pathnew[PATH_MAX_SIZE];     // used by the rename command
 
char            string[128];                // used by snprintf() for debug

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

/////////////////////////////////////////////
static void cmd_cat( int argc , char **argv )
{
    struct stat    st;
    int            fd;
    int            size;
    char         * buf;

#if DEBUG_CMD_CAT
snprintf( string , 128 , "[ksh] %s enters" , __FUNCTION__);
display_string( string );
#endif

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

        sem_post( &semaphore );
	    return;
    }

    strcpy( pathname , argv[1] );

#if DEBUG_CMD_CAT
snprintf( string , 128 , "[ksh] %s : after strcpy" , __FUNCTION__ );
display_string( string );
#endif

    // open the file
    fd = open( pathname , O_RDONLY , 0 );
    if (fd < 0) 
    {
	    printf("  error: cannot open file <%s>\n", pathname );

        sem_post( &semaphore );
	    return;
    }

#if DEBUG_CMD_CAT
snprintf( string , 128 , "[ksh] %s : file %s open", __FUNCTION__, pathname );
display_string( string );
#endif

    // get file stats 
    if ( stat( pathname , &st ) == -1)
    {
	    printf("  error: cannot stat <%s>\n", pathname );

	    close(fd);
        sem_post( &semaphore );
	    return;
    }

   	if ( S_ISDIR(st.st_mode) )
    {
   	    printf("  error: <%s> is a directory\n", pathname );

	    close(fd);
        sem_post( &semaphore );
	    return;
    }

    // get file size
    size = st.st_size;

#if DEBUG_CMD_CAT
snprintf( string , 128 , "[ksh] %s : size = %d",
__FUNCTION__, size );
display_string( string );
#endif

    if( size == 0 )
    {
   	    printf("  error: size = 0 for <%s>\n", pathname );

	    close(fd);
        sem_post( &semaphore );
	    return;
    }

    // mapping type is MAP_FILE when MAP_ANON and MAP_REMOTE are not specified
    buf = mmap( NULL , size , PROT_READ|PROT_WRITE , MAP_PRIVATE , fd , 0 );

    if ( buf == NULL )
    {
  	    printf("  error: cannot map file <%s>\n", pathname );

	    close(fd);
        sem_post( &semaphore );
	    return;
    }

#if DEBUG_CMD_CAT
snprintf( string , 128 , "[ksh] %s : mapped file %d to buffer %x",
__FUNCTION__, fd , buf );
display_string( string );
#endif

    // display the file content on TXT terminal
    write( 1 , buf , size );

    // unmap the file
    if( munmap( buf , size ) )
    {
  	    printf("  error: cannot unmap file <%s>\n", pathname );
    }

#if DEBUG_CMD_CAT
snprintf( string , 128 , "[ksh] %s : unmapped file %d from buffer %x", 
__FUNCTION__, fd , buf );
display_string( string );
#endif

    // close the file
	if( close( fd ) )
    {
  	    printf("  error: cannot close file <%s>\n", pathname );
    }

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

}   // end cmd_cat()

////////////////////////////////////////////
static void cmd_cd( int argc , char **argv )
{
	if (argc != 2)
    {
		printf("  usage: cd pathname\n");
	}
    else
    {
	    strcpy( pathname , argv[1] );

        // call the relevant syscall
        if( chdir( pathname ) )
        {
            printf("  error: cannot found <%s> directory\n", pathname );
        }
    }

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

}   // end cmd_cd()

/////////////////////////////////////////
static void cmd_cp(int argc, char **argv)
{
  	int          src_fd;
    int          dst_fd;
  	int          size;          // source file size
  	int          bytes;         // number of transfered bytes
  	char         buf[4096];
  	struct stat  st;

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] enter %s" , __FUNCTION__);
display_string( string );
#endif

	if (argc != 3) 
    {
        src_fd = -1;
        dst_fd = -1;
		printf("  usage: cp src_pathname dst_pathname\n");
        goto cmd_cp_exit;
	}

    // open the src file 
    strcpy( pathname , argv[1] );
    src_fd = open( pathname , O_RDONLY , 0 );

    if ( src_fd < 0 ) 
    {
        dst_fd = -1;
	    printf("  error: cannot open <%s>\n", argv[1] );
	    goto cmd_cp_exit;
    }

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] %s : file %s open", __FUNCTION__, argv[1] );
display_string( string );
#endif

    // get file stats
    if ( stat( pathname , &st ) )
    {
        dst_fd = -1;
	    printf("  error: cannot stat <%s>\n", argv[1] );
	    goto cmd_cp_exit;
    }

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] %s : got stats for %s", __FUNCTION__, argv[1] );
display_string( string );
#endif

	if ( S_ISDIR(st.st_mode) )
    {
        dst_fd = -1;
		printf("  error: <%s> is a directory\n", argv[1] );
		goto cmd_cp_exit;
	}

    // get src file size
	size = st.st_size;

	// open the dst file 
    strcpy( pathname , argv[2] );
	dst_fd = open( pathname , O_CREAT|O_TRUNC|O_RDWR , 0 );

	if ( dst_fd < 0 ) 
    {
		printf("  error: cannot open <%s>\n", argv[2] );
		goto cmd_cp_exit;
	}

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] %s : file %s open", __FUNCTION__, argv[2] );
display_string( string );
#endif

	if ( stat( pathname , &st ) )
    {
		printf("  error: cannot stat <%s>\n", argv[2] );
		goto cmd_cp_exit;
	}

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] %s : got stats for %s", __FUNCTION__, argv[2] );
display_string( string );
#endif

	if ( S_ISDIR(st.st_mode ) ) 
    {
		printf("  error: <%s> is a directory\n", argv[2] );
		goto cmd_cp_exit;
	}

	bytes = 0;

	while (bytes < size)
	{
		int len = ((size - bytes) < 4096) ? (size - bytes) : 4096;

		// read the source 
		if ( read( src_fd , buf , len ) != len )
        {
			printf("  error: cannot read from file <%s>\n", argv[1] );
			goto cmd_cp_exit;
		}

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] %s : read %d bytes from %s", __FUNCTION__, len, argv[1] );
display_string( string );
#endif

		// write to the destination 
		if ( write( dst_fd , buf , len ) != len )
        {
			printf("  error: cannot write to file <%s>\n", argv[2] );
			goto cmd_cp_exit;
		}

#if DEBUG_CMD_CP
snprintf( string , 128 , "[ksh] %s : write %d bytes to %s", __FUNCTION__, len, argv[2] );
display_string( string );
#endif

		bytes += len;
	}

cmd_cp_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 )
{
    if( argc < 2 )
    {
        printf("  usage: display  vmm      cxy      pid      mapping\n"
               "         display  sched    cxy      lid\n"              
               "         display  process  cxy\n"              
               "         display  txt      txtid\n"              
               "         display  vfs\n"              
               "         display  chdev\n"              
               "         display  dqdt\n"              
               "         display  locks    pid      trdid\n"
               "         display  barrier  pid\n"
               "         display  mapper   path     page     nbytes\n"
               "         display  fat      min      nslots\n"
               "         display  fat      cxy      0\n"
               "         display  socket   pid      fdid\n" );
    }
    ////////////////////////////////////
    else if( strcmp( argv[1] , "vmm" ) == 0 )
    {
        if( argc != 5 )
        {
		    printf("  usage: display vmm cxy pid mapping\n");
	    }
        else
        {
	        unsigned int cxy = atoi(argv[2]);
	        unsigned int pid = atoi(argv[3]);
	        unsigned int map = atoi(argv[4]);

            if( display_vmm( cxy , pid , map ) )
            {
                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");
	    }
        else
        {
	        unsigned int cxy = atoi(argv[2]);
	        unsigned int 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");
	    }
        else
        {
	        unsigned int cxy = atoi(argv[2]);

            if( display_cluster_processes( cxy , 0 ) )
            {
                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");
	    }
        else
        {
	        unsigned int txtid = atoi(argv[2]);

            if( display_txt_processes( txtid ) )
            {
                printf("  error: illegal argument txtid = %d\n", txtid );
            }
        }
    }
    /////////////////////////////////////////
    else if( strcmp( argv[1] , "vfs" ) == 0 )
    {
        if( argc != 2 )
        {
		    printf("  usage: display vfs\n");
	    }
        else
        {
            display_vfs();
        }
    }
    //////////////////////////////////////////
    else if( strcmp( argv[1] , "chdev" ) == 0 )
    {
        if( argc != 2 )
        {
		    printf("  usage: display chdev\n");
	    }
        else
        {
            display_chdev();
        }
    }
    //////////////////////////////////////////
    else if( strcmp( argv[1] , "dqdt" ) == 0 )
    {
        if( argc != 2 )
        {
		    printf("  usage: display dqdt\n");
	    }
        else
        {
            display_dqdt();
        }
    }
    ///////////////////////////////////////////
    else if( strcmp( argv[1] , "locks" ) == 0 )
    {
        if( argc != 4 )
        {
		    printf("  usage: display locks pid trdid\n");
	    }
        else
        {
	        unsigned int pid   = atoi(argv[2]);
            unsigned int trdid = atoi(argv[3]);

            if( display_busylocks( pid , trdid ) )
            {
                printf("  error: illegal arguments pid = %x / trdid = %x\n", pid, trdid );
            }
        }
    }
    /////////////////////////////////////////////////
    else if( strcmp( argv[1] , "barrier" ) == 0 )
    {
        if( argc != 3 )
        {
		    printf("  usage: display barrier pid\n");
	    }
        else
        {
	        unsigned int pid   = atoi(argv[2]);

            if( display_barrier( pid ) )
            {
                printf("  error: illegal arguments pid = %x\n", pid );
            }
        }
    }
    ///////////////////////////////////////////
    else if( strcmp( argv[1] , "mapper" ) == 0 )
    {
        if( argc != 5 )
        {
		    printf("  usage: display mapper path page_id nbytes\n");
	    }
        else
        {
	        unsigned int page_id   = atoi(argv[3]);
            unsigned int nbytes    = atoi(argv[4]);

            if( display_mapper( argv[2] , page_id, nbytes ) )
            {
                printf("  error: cannot display page %d of mapper %s\n", page_id, argv[2] );
            }
        }
    }
    ///////////////////////////////////////////
    else if( strcmp( argv[1] , "fat" ) == 0 )
    {
        if( argc != 4 )
        {
		    printf("  usage: display fat  min_slot  nb_slots\n");
	    }
        else
        {
	        unsigned int min_slot = atoi(argv[2]);
            unsigned int nb_slots = atoi(argv[3]);

            if( display_fat( min_slot, nb_slots ) )
            {
                printf("  error: cannot display fat\n");
            }
        }
    }
    ///////////////////////////////////////////
    else if( strcmp( argv[1] , "socket" ) == 0 )
    {
        if( argc != 4 )
        {
		    printf("  usage: display socket pid fdid\n");
	    }
        else
        {
	        unsigned int pid   = atoi(argv[2]);
            unsigned int fdid = atoi(argv[3]);

            if( display_socket( pid , fdid ) )
            {
                printf("  error: cannot found socket[%x,%d]\n", pid, fdid );
            }
        }
    }
    ////
    else
    {
        printf("  error: undefined display request : %s\n", argv[1] ); 
    }        

    // 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]);
	}
    else
    {
        pid = atoi( argv[1] );   

        if( pid == 0 )
        {  
		    printf("  error: PID cannot be 0\n" );
	    }
        else 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]);
	}
    else
    {
        printf("available commands:\n");
	    for (i = 0 ; command[i].name ; i++) 
        {
		    printf("\t%s\t : %s\n", command[i].name , command[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]);
	}
    else
    {
	    pid = atoi( argv[1] );

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

	    else 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
    unsigned int         background;         // background execution if non zero
    unsigned int         placement;          // placement specified if non zero
    unsigned int         cxy;                // target cluster if placement specified

#if DEBUG_CMD_LOAD
snprintf( string , 128 , "[ksh] enter %s" , __FUNCTION__);
display_string( string );
#endif

	if( (argc < 2) || (argc > 4) )  
    {
		printf("  usage: %s pathname [cxy] [&]\n", argv[0] );
	}
    else
    {
	    strcpy( pathname , argv[1] );

        if( argc == 2 )
        {
            background = 0;
            placement  = 0;
            cxy        = 0;
        }
        else if( argc == 3 )
        {
            if( (argv[2][0] == '&') && (argv[2][1] == 0) )
            {
                background = 1;
                placement  = 0;
                cxy        = 0;
            }
            else 
            {
                background = 0;
                placement  = 1;
                cxy        = atoi( argv[2] );
            }
        }
        else  // argc == 4
        { 
            background = ( (argv[3][0] == '&') && (argv[3][1] == 0) );
            placement  = 1;
            cxy        = atoi( argv[2] );
        }

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

#if DEBUG_CMD_LOAD
snprintf( string , 128 , "[ksh] %s : <%s> / bg %d / place %d / cxy %x",
__FUNCTION__, argv[1], background, placement, cxy );
display_string( string );
#endif

        // set target cluster if required
        if( placement ) place_fork( cxy );

        // 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");
        }
        else if (ret_fork == 0) // it is the CHILD process 
        {

#if DEBUG_CMD_LOAD
snprintf( string , 128 , "[ksh] %s : child (%x) after fork, before exec",
__FUNCTION__ , getpid() );
display_string( string );
#endif

            // CHILD process exec NEW process
            ret_exec = execve( pathname , NULL , NULL );

#if DEBUG_CMD_LOAD
snprintf( string , 128 , "[ksh] %s : child (%x) after exec / ret_exec %x",
__FUNCTION__ , getpid(), ret_exec );
display_string( string );
#endif

            // 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 DEBUG_CMD_LOAD
snprintf( string , 128 , "[ksh] %s : ksh (%x) after fork / ret_fork %x",
__FUNCTION__, getpid(), ret_fork );
display_string( string );
#endif
            // when the new process is launched in background, the KSH process 
            // takes the TXT ownership, and release the semaphore to get the next command.
            // Otherwise, the child process keep the TXT ownership, and the semaphore will
            // be released by the KSH main thread when the child process exit

            if( background )    //  KSH must keep TXT ownership
            {
                // get back the TXT ownership
                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;

	if (argc != 1)
    {
		printf("  usage: %s\n", argv[0], argc ); 
	}
    else
    {
	    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 )
{
    struct dirent  * entry;
    DIR            * dir;

#if DEBUG_CMD_LS
snprintf( string , 128 , "[ksh] enter %s" , __FUNCTION__);
display_string( string );
#endif

	if (argc > 2 )
    {
		printf("  usage: ls [path]\n");
	}
    else
    {
        // handle case with no argument

        // get target directory path
        if ( argc == 1 ) strcpy( pathname , "." );
        else             strcpy( pathname , argv[1] );

        // open target directory
	    dir = opendir( pathname );

#if DEBUG_CMD_LS
snprintf( string , 128 , "[ksh] %s : directory <%s> open / DIR %x",
__FUNCTION__, pathname , dir );
display_string( string );
#endif

        if( dir == NULL)
	    {
		    printf("  error : directory <%s> not found\n", pathname );

            sem_post( &semaphore );
            return;
	    }

        // loop on directory entries    
    	while ( (entry = readdir(dir)) != NULL )
	    {
		    printf("%s\n", entry->d_name);
	    }

        // close target directory
	    closedir( dir );

#if DEBUG_CMD_LS
snprintf( string , 128 , "[ksh] %s : directory <%s> closed",
__FUNCTION__, pathname );
display_string( string );
#endif

    }

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

} // end cmd_ls()

///////////////////////////////////////////////
static void cmd_mkdir( int argc , char **argv )
{
	if (argc != 2)
    {
		printf("  usage: mkdir pathname\n");
	}
    else
    {
        strcpy( pathname , argv[1] );

        mkdir( pathname , 0x777 );
    }

    // 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: mv old_pathname new_pathname\n");
	}
    else
    {
        strcpy( pathname , argv[1] );
        strcpy( pathnew  , argv[2] );

        // call the relevant syscall
        if( rename( pathname , pathnew ) )
        {
            printf("  error: unable to rename <%s> to <%s>\n", pathname , pathnew );
        }
    }

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

}  // end cmd_mv


////////////////////////////////////////////
static void cmd_ps( int argc , char **argv )
{
    unsigned int x_size;
    unsigned int y_size;
    unsigned int x;
    unsigned int y;

#if DEBUG_CMD_PS
snprintf( string , 128 , "[ksh] enter %s" , __FUNCTION__);
display_string( string );
#endif

	if (argc != 1)
    {
		printf("  usage: %s\n", argv[0]);
	}
    else
    {
        // get platform config
        hard_config_t  config;
        get_config( &config );
        x_size = config.x_size;
        y_size = config.y_size;

        // scan all clusters
        for( x = 0 ; x < x_size ; x++ )
        {
            for( y = 0 ; y < y_size ; y++ )
            {

#if DEBUG_CMD_PS
snprintf( string , 128 , "\n[ksh] %s : call display_cluster_process()", __FUNCTION__ );
display_string( string );
#endif

                // display only owned processes
                display_cluster_processes( HAL_CXY_FROM_XY(x,y), 1 ); 
            }
        }
    }

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

}  // end cmd_ps()

/////////////////////////////////////////////
static void cmd_pwd( int argc , char **argv )
{
	if (argc != 1)
    {
		printf("  usage: %s\n", argv[0]);
	}
    else 
    {
        if ( getcwd( pathname , PATH_MAX_SIZE ) ) 
        {
		    printf("  error: unable to get current directory\n");
	    }
        else 
        {
		    printf("%s\n", pathname );
	    }
    }

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

}  // end cmd_pwd()

////////////////////////////////////////////
static void cmd_rm( int argc , char **argv )
{
	if (argc != 2)
    {
		printf("  usage: %s pathname\n", argv[0]);
	}
    else
    {
	    strcpy( pathname , argv[1] );

        if ( unlink( pathname ) )
        {
		    printf("  error: unable to remove <%s>\n", pathname );
	    }
    }

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

}  // end cmd_rm()

///////////////////////////////////////////////
static void cmd_stat( int argc , char **argv )
{
    struct stat    st;
    unsigned int   size;

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

        if ( stat( pathname , &st ) )
        {
		    printf("  error: cannot stat <%s>\n", argv[2] );
		}
        else
        {
            // get file size
            size = st.st_size;

            // print file stat info
            printf("   <%s> : %d bytes\n", pathname, size );
        }
	}

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

}  // end cmd_stat()

///////////////////////////////////////////////
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");
	}
    else
    {
        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");
	}
    else
    {
        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 command[] =
{
	{ "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     },
	{ "ps",      "display all processes",                           cmd_ps      },
	{ "rm",      "remove a file from file system",                  cmd_rm      },
	{ "rmdir",   "remove a directory from file system",             cmd_rmdir   },
	{ "stat",    "print infos on a given file",                     cmd_stat    },
	{ "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 __attribute__ ((noinline)) execute( char * buf )
{
	int    argc = 0;
	char * argv[MAX_ARGS];
	int    i;
	int    len = strlen(buf);

#if DEBUG_EXECUTE
snprintf( string , 128 , "[ksh] enter %s for command <%s>" , __FUNCTION__ , buf );
display_string( string );
#endif

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

    // check command
	if (argc == 0)
    {
        // release semaphore to get next command
        sem_post( &semaphore );
    }

#if DEBUG_EXECUTE
snprintf( string , 128 , "\n[ksh] in %s : argc = %d / arg0 = %s / arg1 = %s",
__FUNCTION__ , argc , argv[0], argv[1] );
#endif

    // scan the list of commands to match typed command
    int found = 0;
    for ( i = 0 ; (command[i].name != NULL) && (found == 0) ; i++ )
    {
        if (strcmp(argv[0], command[i].name) == 0)
        {
			command[i].fn(argc, argv);
			found = 1;
		}
    }

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

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



///////////////////////////////
static void interactive( void )
{
	char           c;						// read character
    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           cmd[CMD_MAX_SIZE];		// buffer for one command

/*  direct command to help debug

    int pid = getpid();

    if( pid == 3 )
    {
        if( sem_wait( &semaphore ) )
        {
            printf("\n[ksh %d] error : cannot found semafore\n" );
            exit( 1 );
        }
        else
        {
            strcpy( cmd , "load bin/user/tcp_server.elf" );
            printf("[ksh] %s\n", cmd );
            execute( cmd );
        }
    }
    else if( getpid() == 4 )
    {
        if( sem_wait( &semaphore ) )
        {
            printf("\n[ksh %d] error : cannot found semafore\n" );
            exit( 1 );
        }
        else
        {
            strcpy( cmd , "load bin/user/tcp_client.elf" );
            printf("[ksh] %s\n", cmd );
            execute( cmd );
        }
    }

*/


	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

    // take the semaphore for the first command
    if ( sem_wait( &semaphore ) )
    {
        printf("\n[ksh error] cannot found semafore\n" );
        exit( 1 );
    }

    // display prompt for the first command
    printf("\n[ksh] ");

    // external loop on the commands / the interactive thread do not exit this loop
	while (1)
	{
	    // initialize command buffer
	    count       = 0;
	    state       = NORMAL;
        end_command = 0;

#if DEBUG_INTER 
snprintf( string , 128 , "[ksh] %s : request a new command", __FUNCTION__ );
display_string( string );
#endif

        // 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
					    cmd[count] = 0;
                        count++;
#if DEBUG_INTER
snprintf( string , 128 , "[ksh] %s : get command <%s>", __FUNCTION__, cmd );
display_string( string );
#endif
                        // register command in log_entries[] array
					    strncpy( log_entries[ptw].buf , cmd , count );
					    log_entries[ptw].count = count;
					    ptw = (ptw + 1) % LOG_DEPTH;
					    ptr = ptw;

#if DEBUG_INTER
snprintf( string , 128 , "[ksh] %s : execute <%s>", __FUNCTION__, cmd );
display_string( string );
#endif
                        // echo character
                        putchar( c );

					    // execute command
					    execute( cmd );
				    }
                    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(cmd) - 1) )
				    {
                        // register character in command buffer
					    cmd[count] = c;
					    count++;

                        // echo character
                        putchar( c );
					}
                    else
                    {
		                printf("\none command cannot exceed %d characters\n", sizeof(cmd) );
                    }
				}
			}
			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 cmd pointer left
				{
					if (count > 0)
					{
						printf("\b");
						count--;
					}

					// get next user char
					state = NORMAL;
				}
				else if (c == 'C')   // valid  RIGHT sequence => move cmd pointer right
				{
					if (count < sizeof(cmd) - 1)
					{
						printf("%c", cmd[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 cmd
					ptr = (ptr - 1) % LOG_DEPTH;
					strcpy(cmd, log_entries[ptr].buf);
					count = log_entries[ptr].count - 1;

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

					// 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 cmd
					ptr = (ptr + 1) % LOG_DEPTH;
					strcpy(cmd, log_entries[ptr].buf);
					count = log_entries[ptr].count;

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

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

#if DEBUG_INTER 
snprintf( string , 128 , "\n[ksh] %s : complete <%s> command", __FUNCTION__, cmd );
display_string( string );
#endif

        // block interactive thread if KSH loose TXT ownership
        if ( sem_wait( &semaphore ) )
        {
            printf("\n[ksh error] cannot found semafore\n" );
            exit( 1 );
        }

        // display prompt for next command
        printf("\n[ksh] ");

	}  // end external while loop on commands

}  // end interactive()

////////////////
int main( void )
{
    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          parent_pid;      // parent process identifier (i.e. this process)
    int          child_pid;       // child process identifier
    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_id( &cxy , &lid );

#if DEBUG_MAIN
snprintf( string , 128 , "\n[ksh] main thread started on core[%x,%d]", cxy , lid ); 
display_string( string );
#endif
    
    // initializes the semaphore used to synchronize with interactive thread
    if ( sem_init( &semaphore , 0 , 1 ) )
    {
        printf("\n[KSH ERROR] cannot initialize semaphore\n" );
        exit( 1 ); 
    }

#if DEBUG_MAIN
snprintf( string , 128 , "\n[ksh] main initialized semaphore" ); 
display_string( string );
#endif
    
    // 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 ); 
#if DEBUG_MAIN
snprintf( string , 128 , "\n[ksh] main thread launched interactive thread %x", trdid ); 
display_string( string );
#endif

    // signal INIT process
    kill( 1 , SIGCONT );
    
    // enter infinite loop monitoring children processes termination 
    while( 1 )
    {
        // wait children termination
        child_pid = wait( &status );

        if( DEBUG_MAIN )
        {
            if(WIFEXITED  (status)) printf("\n[ksh] child process %x exit\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);
        }

        // 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()


