/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  WHEN YOU FIGURE OUT WHAT THIS FILE DOES, PLEASE
*               DESCRIBE IT HERE!
*
****************************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <process.h>
extern int KbdHandle;

#define INCL_SUB
#define INCL_DOSQUEUES
#define INCL_DOSPROCESS
#include <os2.h>

#define START "#####"
#define END "#####"

#define BUF_SIZE                        512
#define STACKSIZE                       2048
char                                    EchoStack[STACKSIZE];
TID                                     EchoId;
char                                    WaiterStack[STACKSIZE];
TID                                     WaiterId;
RESULTCODES                             ChildId;
HFILE                                   StdinRdHdl;
HFILE                                   StdinWrHdl;
HFILE                                   StdoutRdHdl;
HFILE                                   StdoutWrHdl;
char                                    PromptTail[BUF_SIZE];
char volatile                           LastCh;
char volatile                           CharWritten;


extern  char    far *GetEnv( char far *, int );
unsigned char far * AliasList;

typedef struct {
        int     input;
        int     output;
        } LENGTH;

extern void InitRetrieve( char far * );
extern void StringIn( char far *, LENGTH far *, int, int );

void far Waiter(void)
{
/*
    This routine just waits until the spawned CMD.EXE terminates and
    kills off all threads in this process
*/
    PID         dummy;
    RESULTCODES res_code;

    DosCWait( DCWA_PROCESSTREE, DCWW_WAIT, &res_code,
        &dummy, ChildId.codeTerminate );
    DosClose( StdinWrHdl );
    DosClose( StdoutRdHdl );
    DosClose( KbdHandle );
    exit( res_code.codeResult );
}


void SetOurCwd( char * buff )
{
/*
    Parse up all the output generated by the #####$P##### prompt prefix.
*/
    char        *p;

    for( p = buff; *p && *p != '#' ; ++p );
    *p = '\0';
    strupr( buff );
    DosSelectDisk( (*buff - 'A') + 1 );
    DosChdir( buff+2, 0 );
}

static char ReadCh()
{
/*
    Buffered read of the output from CMD.EXE.
*/
    static char buff[BUF_SIZE];
    static USHORT pos;
    static USHORT len;

    if( pos == len ) {
        WriteCh( -1 ); // flush to keep input and output in sync
        DosRead( StdoutRdHdl, buff, BUF_SIZE, &len );
        pos = 0;
    }
    return( buff[ pos++ ] );
}

static void WriteCh( int ch )
{
/*
    Buffered write to stdout. -1 means flush
*/
    static char buff[BUF_SIZE];
    static USHORT pos;
    USHORT x;

    if( ch == -1 ) {
        if( pos == 0 ) return;
        CharWritten = 1;
        LastCh = buff[ pos-1 ];
        DosWrite( 1, buff, pos, &x );
        pos = 0;
        return;
    }
    CharWritten = 1;
    buff[ pos++ ] = ch;
    if( pos == BUF_SIZE ) {
        WriteCh( -1 );
    }
}

void far Echo(void)
{
/*
    This routine catches all the output from CMD.EXE and supresses the
    extraneous junk. It also parses the output from the prompt,
    sets our current drive and directory to be the same as our child CMD.EXE
    This allows us to track its current working directory and
    drive to make filename completion possible.
    It is assumed that prompts have the following prefix: #####$P#####
*/
    static char buff[512];
    char        *p;
    int         endlen = strlen( END );
    int         startlen = strlen( START );

    for( ;; ) {
        WriteCh( -1 );
        p = buff;
        for( ;; ) {
            *p = ReadCh();
            if( *p == '#' ) break;
            WriteCh( *p );
        }
        for( ;; ) {
            *++p = ReadCh();
            if( *p != '#' ) break;
        }
        *++p = '\0';
        if( strncmp( buff, START, startlen ) != 0 ) {
            for( p = buff; *p != '\0'; ++p ) {
                WriteCh( *p );
            }
            continue;
        }
        buff[0] = *--p;
        p = buff;
        for( ;; ) {
            *++p = ReadCh();
            if( strncmp( (p - endlen) + 1, END, endlen ) == 0 ) break;
            if( *p == '\n' ) break;
        }
        if( *p == '\n' )  {
            *++p = '\0';
            for( p = buff; *p != '\0'; ++p ) {
                WriteCh( *p );
            }
            continue;
        }
        *++p = '\0';
        SetOurCwd( buff );
    }
}


void main( void )
{
/*
    Here's the idea here. Create two pipes, dup them on top of
    stdin and stdout, then spawn CMD.EXE. This lets us catch all
    it's output, and feed it all it's input. After every command,
    we scan for the prompt containing the current working directory
    and use it to update our working directory.
    See Echo() for details about this.
*/

    HFILE       stdinHdl,saveStdinHdl;
    HFILE       stdoutHdl,saveStdoutHdl;
    USHORT      x;
    LENGTH      l;
    char        *src,*dst;
    char        buffer[ BUF_SIZE ];
    char        cmdline[ BUF_SIZE ];

    buffer[0] = 0;
    InitRetrieve( buffer );
    memset( buffer, 0, sizeof( buffer ) );
    getcmd( buffer );
    src = buffer;
    while( *src == ' ' ) ++src;
    dst = cmdline;
    if( *src == '\0' ) {
        strcpy( dst, "cmd.exe" );
        dst += strlen( dst );
    } else {
        while( *src != ' ' && *src != '\0' ) {
            *dst++ = *src++;
        }
    }
    *dst++ = '\0';
    strcpy( dst, "/Q " );
    strcat( dst, src );

    DosMakePipe( &StdinRdHdl, &StdinWrHdl, 1 );
    DosMakePipe( &StdoutRdHdl, &StdoutWrHdl, BUF_SIZE );
    stdinHdl = 0;
    stdoutHdl = 1;
    saveStdinHdl = 0xFFFF;
    saveStdoutHdl = 0xFFFF;

    DosDupHandle( stdinHdl, &saveStdinHdl );
    DosDupHandle( stdoutHdl, &saveStdoutHdl );

    DosDupHandle( StdinRdHdl, &stdinHdl );
    DosDupHandle( StdoutWrHdl, &stdoutHdl );

    DosCreateThread( Echo, &EchoId, EchoStack+STACKSIZE );

    DosExecPgm( NULL, 0, EXEC_ASYNCRESULT, cmdline, 0, &ChildId, cmdline );

    DosDupHandle( saveStdinHdl, &stdinHdl );
    DosDupHandle( saveStdoutHdl, &stdoutHdl );

    DosCreateThread( Waiter, &WaiterId, WaiterStack+STACKSIZE );
    for(;;) {
        // this write blocks till last command has been read
        DosWrite( StdinWrHdl, "  ", 2, &x );
        l.input = 4094;
        StringIn( buffer, &l, 1, 5 );
//      DosWrite( 1, "\r\n", 2, &x );
        buffer[ l.output ] = '\r';
        buffer[ l.output+1 ] = '\n';
        LastCh = 0;
        CharWritten = 0;
        DosWrite( StdinWrHdl, buffer, l.output+2, &x );
        while( CharWritten == 0 ) {
            DosSleep( 50 );
        }
    }
}
