/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1997 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*++

Copyright 1993 - 1997 Microsoft Corporation

Module Name:

    Remote.c

Abstract:

    This module contains the main() entry point for Remote.
    Calls the Server or the Client depending on the first parameter.


Author:

    Rajivendra Nath  2-Jan-1993

Environment:

    Console App. User mode.

Revision History:

--*/


#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "Remote.h"

char   HostName[HOSTNAMELEN];
char*  ChildCmd;
char*  PipeName;
char*  ServerName;
char * DaclNames[ MAX_DACL_NAMES ];
DWORD  DaclNameCount = 0;
char * DaclDenyNames[ MAX_DACL_NAMES ];
DWORD  DaclDenyNameCount = 0 ;
HANDLE MyStdOut;
HANDLE hAttachedProcess = INVALID_HANDLE_VALUE;
HANDLE hAttachedWriteChildStdIn = INVALID_HANDLE_VALUE;
HANDLE hAttachedReadChildStdOut = INVALID_HANDLE_VALUE;

BOOL   IsAdvertise;
DWORD  ClientToServerFlag;
BOOL   bForceTwoPipes;

typedef struct _tagKeywordAndColor
{
    char *szKeyword;
    WORD color;
    struct _tagKeywordAndColor *next;
} KeywordAndColor;
KeywordAndColor *pKeyColors;

char* ColorList[]={"black" ,"blue" ,"green" ,"cyan" ,"red" ,"purple" ,"yellow" ,"white",
                   "lblack","lblue","lgreen","lcyan","lred","lpurple","lyellow","lwhite"};

typedef enum { LINE_TOO_LONG } WARNING_MESSAGE;

VOID
DisplayWarning(
    WARNING_MESSAGE warn
    );

WORD
GetColorNum(
    char* color
    );

VOID
SetColor(
    WORD attr
    );

BOOL
GetColorFromBuffer(
    char **ppBuffer,
    char *pBufferInvalid,
    WORD *color,
    BOOL bStayOnLine
    );

VOID
AssocKeysAndColors(
    KeywordAndColor **ppKeyAndColors,
    char *szFileName
    );

BOOL
GetNextConnectInfo(
    char** SrvName,
    char** PipeName
    );



CONSOLE_SCREEN_BUFFER_INFO csbiOriginal;

int
__cdecl
main(
    int    argc,
    char** argv
    )
{
    WORD  RunType;              // Server or Client end of Remote
    DWORD len=HOSTNAMELEN;
    int   i, FirstArg;

    char  sTitle[120];          // New Title
    char  orgTitle[200];        // Old Title
    BOOL  bPromptForArgs=FALSE; // Is /P option
    WORD  wAttrib;              // Console Attributes
    int   privacy;              // Allows exposing or hidng sessions to remote /q
    BOOL  Deny ;

    GetComputerName((LPTSTR)HostName,&len);

    MyStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if (GetConsoleScreenBufferInfo(MyStdOut,&csbiOriginal)) {

        wAttrib = csbiOriginal.wAttributes;
        if (!GetConsoleTitle(orgTitle,sizeof(orgTitle))) {
            orgTitle[0] = 0;
        }

    } else {

        //
        // either stdout is a pipe, or it wasn't opened for
        // GENERIC_READ along with GENERIC_WRITE, in which
        // case our color manipulations will work so we need
        // to pick default colors.
        //

        wAttrib = FOREGROUND_GREEN |
                  FOREGROUND_INTENSITY;

        orgTitle[0] = 0;
    }

    privacy = PRIVACY_DEFAULT;

    pKeyColors = NULL;


    //
    // Parameter Processing
    //
    // For Server:
    // Remote /S <Executable>  <PipeName> [Optional Params]
    //
    // For Client:
    // Remote /C <Server Name> <PipeName> [Optional Params]
    // or
    // Remote /P
    // This will loop continously prompting for different
    // Servers and Pipename


    if ((argc<2)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
    {

        DisplayServerHlp();
        DisplayClientHlp();
        return(1);
    }

    switch(argv[1][1])
    {
    case 'c':
    case 'C':

        //
        // Is Client End of Remote
        //

        if ((argc<4)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
        {

            DisplayServerHlp();
            DisplayClientHlp();
            return(1);
        }

        ServerName=argv[2];
        PipeName=argv[3];
        FirstArg=4;
        RunType=RUNTYPE_CLIENT;
        break;


    case 'q':
    case 'Q':

        //
        //  Query for possible conexions
        //


        if ((argc != 3)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
        {

            DisplayServerHlp();
            DisplayClientHlp();
            return(1);
        }

        QueryRemotePipes(argv[2]);  //  Send ServerName as a param
        return(0);


    case 'p':
    case 'P':

        //
        // Is Client End of Remote
        //

        bPromptForArgs=TRUE;
        RunType=RUNTYPE_CLIENT;
        FirstArg=2;
        break;


    case 's':
    case 'S':
        //
        // Is Server End of Remote
        //
        if ((argc<4)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
        {

            DisplayServerHlp();
            DisplayClientHlp();
            return(1);
        }

        ChildCmd=argv[2];
        PipeName=argv[3];
        FirstArg=4;

        RunType=REMOTE_SERVER;
        break;


    case 'a':
    case 'A':
        //
        // Is Server End of Remote Attaching to existing process.
        //
        if ((argc<7)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
        {

            DisplayServerHlp();
            DisplayClientHlp();
            return(1);
        }

        hAttachedProcess = (HANDLE)IntToPtr(atoi(argv[2]));
        hAttachedWriteChildStdIn = (HANDLE)IntToPtr(atoi(argv[3]));
        hAttachedReadChildStdOut = (HANDLE)IntToPtr(atoi(argv[4]));
        ChildCmd=argv[5]; // for display only
        PipeName=argv[6];
        FirstArg=7;

        RunType = REMOTE_SERVER;
        privacy = PRIVACY_VISIBLE;  // presumably ntsd/*kd
        break;

    default:
        DisplayServerHlp();
        DisplayClientHlp();
        return(1);
    }

    if (RunType==REMOTE_SERVER)
    {
        //
        // Base Name of Executable
        // For setting the title
        //

        char *tcmd=ChildCmd;

        while ((*tcmd!=' ')      && (*tcmd!=0))    tcmd++;
        while ((tcmd > ChildCmd) && (*tcmd!='\\')) tcmd--;
        if (*tcmd=='\\') tcmd++;
        sprintf(sTitle,"%-41.40s [Remote /C %s \"%.30s\"]",tcmd,HostName,PipeName);
    }

    //
    //Process Common (Optional) Parameters
    //

    for (i=FirstArg;i<argc;i++)
    {

        if ((argv[i][0]!='/')&&(argv[i][0]!='-'))
        {
            printf("Invalid parameter %s:Ignoring\n",argv[i]);
            continue;
        }

        switch(argv[i][1])
        {
        case 'l':    // Only Valid for client End
        case 'L':    // Max Number of Lines to recieve from Server
            i++;
            if (i>=argc)
            {
                printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
                break;
            }
            LinesToSend=(DWORD)atoi(argv[i])+1;
            break;

        case 't':    // Title to be set instead of the default
        case 'T':
            i++;
            if (i>=argc)
            {
                printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
                break;
            }
            sprintf(sTitle,"%s",argv[i]);
            break;

        case 'b':    // Background color
        case 'B':
            i++;
            if (i>=argc)
            {
                printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
                break;
            }
            {
                WORD col=GetColorNum(argv[i]);
                if (col!=0xffff)
                {
                    wAttrib=col<<4|(wAttrib&0x000f);
                }
                break;
            }

        case 'f':    // Foreground color
        case 'F':
            i++;
            if (i>=argc)
            {
                printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
                break;
            }
            {
                WORD col=GetColorNum(argv[i]);
                if (col!=0xffff)
                {
                    wAttrib=col|(wAttrib&0x00f0);
                }
                break;
            }

        case 'k':    // Color "keyword" lines
        case 'K':
            i++;
            // Currently only support client-side coloring
            if (RunType==REMOTE_SERVER)
            {
                printf("%s invalid on server side..Ignoring\n",argv[i-1]);
                break;
            }
            else if (i>=argc)
            {
                printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
                break;
            }
            else
            {
                AssocKeysAndColors( &pKeyColors, argv[i] );
                break;
            }

        case 'v':
        case 'V':
            privacy = PRIVACY_VISIBLE;
            break;

        case '-':
            if( (argv[i][2] == 'v')
                || (argv[i][2] == 'V'))
                privacy = PRIVACY_NOT_VISIBLE;
            else
                printf("Unknown Parameter=%s %s\n",argv[i-1],argv[i]);
            break;

        case 'q':
        case 'Q':
            ClientToServerFlag|=0x80000000;
            break;

        case 'u':
        case 'U':
            if ( (argv[i][2] == 'd') ||
                 (argv[i][2] == 'D' ) )
            {
                Deny = TRUE ;
            }
            else
            {
                Deny = FALSE ;
            }

            i++ ;

            if ( i >= argc )
            {
                printf( "Incomplete Param %s..Ignoring\n", argv[i-1] );
                break;
            }

            if ( Deny )
            {
                if (DaclDenyNameCount == MAX_DACL_NAMES )
                {
                    printf("Too many names specified (max %d).  Ignoring user %s\n",
                            MAX_DACL_NAMES, argv[i] );

                    break;
                }

                DaclDenyNames[ DaclDenyNameCount++ ] = argv[i];

            }
            else
            {
                if (DaclNameCount == MAX_DACL_NAMES )
                {
                    printf("Too many names specified (max %d).  Ignoring user %s\n",
                            MAX_DACL_NAMES, argv[i] );

                    break;
                }

                DaclNames[ DaclNameCount++ ] = argv[i];

            }

            break;

        case '2':
            bForceTwoPipes = TRUE;
            break;

        default:
            printf("Unknown Parameter=%s %s\n",argv[i-1],argv[i]);
            break;

        }

    }

    //
    //Now Set various Parameters
    //

    //
    //Colors
    //

    SetColor(wAttrib);

    if (RunType==RUNTYPE_CLIENT)
    {
        BOOL done=FALSE;
        BOOL gotinfo;

        //
        // Set Client end defaults and start client
        //

        while(!done)
        {
            if (!bPromptForArgs ||
                (gotinfo = GetNextConnectInfo(&ServerName,&PipeName))
               )
            {
                sprintf(sTitle,"Remote /C %s \"%s\"",ServerName,PipeName);
                SetConsoleTitle(sTitle);

                //
                // Start Client (Client.C)
                //
                Client(ServerName,PipeName);
            }
            done = !bPromptForArgs || !gotinfo;
        }
    }

    if (RunType==REMOTE_SERVER)
    {
        if (privacy == PRIVACY_VISIBLE ||
             (privacy == PRIVACY_DEFAULT && IsKdString(ChildCmd))) {

            strcat(sTitle, " visible");
            IsAdvertise = TRUE;
        }

        SetConsoleTitle(sTitle);

        i = OverlappedServer(ChildCmd, PipeName);
    }

    //
    //Reset Colors
    //
    SetColor(csbiOriginal.wAttributes);
    SetConsoleTitle(orgTitle);

    return i;
}

/*************************************************************/
VOID
ErrorExit(
    char* str
    )
{
    extern PSZ pszPipeName;
    DWORD dwErr;

    dwErr = GetLastError();

    printf("REMOTE error %d: %s\n", dwErr, str);

    #if DBG
    {
        char szMsg[1024];

        sprintf(szMsg, "REMOTE error %d: %s\n", dwErr, str);
        OutputDebugString(szMsg);

        if (pszPipeName) {               // ad-hoc:  if server
            if (IsDebuggerPresent()) {
                DebugBreak();
            }
        }
    }
    #endif

    exit(1);
}

/*************************************************************/
VOID
DisplayClientHlp()
{
    printf("\n"
           "   To Start the CLIENT end of REMOTE\n"
           "   ---------------------------------\n"
           "   Syntax : REMOTE /C <ServerName> \"<Unique Id>\" [Param]\n"
           "   Example1: REMOTE /C %s imbroglio\n"
           "            This would connect to a server session on %s with Id\n"
           "            \"imbroglio\" if there is a REMOTE /S <\"Cmd\"> imbroglio\n"
           "            running on %s.\n\n"
           "   Example2: REMOTE /C %s \"name with spaces\"\n"
           "            This would connect to a server session on %s with Id\n"
           "            \"name with spaces\" if there is a REMOTE /S <\"Cmd\"> \"name with spaces\"\n"
           "            running on %s.\n\n"
           "   To Exit: %cQ (Leaves the Remote Server Running)\n"
           "   [Param]: /L <# of Lines to Get>\n"
           "   [Param]: /F <Foreground color eg blue, lred..>\n"
           "   [Param]: /K <Set keywords and colors from file>\n"
           "   [Param]: /B <Background color eg cyan, lwhite..>\n"
           "\n"
           "   Keywords And Colors File Format\n"
           "   -------------------------------\n"
           "   <KEYWORDs - CASE INSENSITIVE>\n"
           "   <FOREGROUND>[, <BACKGROUND>]\n"
           "   ...\n"
           "   EX:\n"
           "       ERROR\n"
           "       black, lred\n"
           "       WARNING\n"
           "       lblue\n"
           "       COLOR THIS LINE\n"
           "       lgreen\n"
           "\n"
           "   To Query the visible sessions on a server\n"
           "   -----------------------------------------\n"
           "   Syntax:  REMOTE /Q %s\n"
           "            This would retrieve the available <Unique Id>s\n"
           "            visible connections on the computer named %s.\n"
           "\n",
           HostName, HostName, HostName,
           HostName, HostName, HostName,
           COMMANDCHAR, HostName, HostName);
}
/*************************************************************/

VOID
DisplayServerHlp()
{
    printf("\n"
           "   To Start the SERVER end of REMOTE\n"
           "   ---------------------------------\n"
           "   Syntax : REMOTE /S <\"Cmd\">     <Unique Id> [Param]\n"
           "   Example1: REMOTE /S \"i386kd -v\" imbroglio\n"
           "            To interact with this \"Cmd\" from some other machine,\n"
           "            start the client end using:  REMOTE /C %s imbroglio\n\n"
           "   Example2: REMOTE /S \"i386kd -v\" \"name with spaces\"\n"
           "            start the client end using:  REMOTE /C %s \"name with spaces\"\n\n"
           "   To Exit: %cK \n"
           "   [Param]: /F  <Foreground color eg yellow, black..>\n"
           "   [Param]: /B  <Background color eg lblue, white..>\n"
           "   [Param]: /U  username or groupname\n"
           "                specifies which users or groups may connect\n"
           "                may be specified more than once, e.g\n"
           "                /U user1 /U group2 /U user2\n"
           "   [Param]: /UD username or groupname\n"
           "                specifically denies access to that user or group\n"
           "   [Param]: /V  Makes this session visible to remote /Q\n"
           "   [Param]: /-V Hides this session from remote /q (invisible)\n"
           "                By default, if \"Cmd\" looks like a debugger,\n"
           "                the session is visible, otherwise not\n"
           "\n",
           HostName, HostName, COMMANDCHAR);
}

VOID
DisplayWarning(
    WARNING_MESSAGE warn
    )
{
    switch ( warn )
    {
        case LINE_TOO_LONG:
            printf( "\n[REMOTE: WARNING: LINE TOO LONG TO PARSE FOR COLOR KEYWORDS]\n" );
            break;
        default:
            printf( "\n[REMOTE: WARNING: UNSPECIFIED PROBLEM COLORING LINE]\n" );
    }
}

WORD
GetColorNum(
    char *color
    )
{
    WORD i;

    _strlwr(color);
    for (i=0;i<16;i++)
    {
        if (strcmp(ColorList[i],color)==0)
        {
            return(i);
        }
    }
    return ((WORD)atoi(color));
}

VOID
SetColor(
    WORD attr
    )
{
    COORD  origin={0,0};
    DWORD  dwrite;
    FillConsoleOutputAttribute
    (
        MyStdOut,attr,csbiOriginal.dwSize.
        X*csbiOriginal.dwSize.Y,origin,&dwrite
    );
    SetConsoleTextAttribute(MyStdOut,attr);
}

BOOL
pColorLine(
    char *sLine,
    int cbLine,
    WORD wDefaultColor,
    WORD *color
    )
{
    KeywordAndColor *pCurKeyColor = NULL;
    char *pString1;
    int cbCmpString;

    pCurKeyColor = pKeyColors;
    while ( pCurKeyColor )
    {
        cbCmpString = strlen( pCurKeyColor->szKeyword );
        pString1 = sLine;
        // Need to do case-insensitive compare
        while ( pString1 <= sLine + cbLine - cbCmpString )
        {
            if ( !_memicmp( (PVOID)pString1,
                            (PVOID)pCurKeyColor->szKeyword,
                            cbCmpString ) )
            {
                *color = pCurKeyColor->color;
                // Check if we are to use default background color
                if ( (0xfff0 & *color) == 0xfff0 )
                    *color = (wDefaultColor & 0x00f0) |
                             (*color & 0x000f);
                return TRUE;
            }

            pString1++;
        }

        // Next keyword/color combination
        pCurKeyColor = pCurKeyColor->next;
    }

    return FALSE;
}

BOOL
pWantColorLines(
    VOID
    )
{
    return ( NULL != pKeyColors );
}

VOID
AssocKeysAndColors(
    KeywordAndColor **ppKeyColors,
    char *szFileName
    )
{
    char szPathName[_MAX_PATH],
         *szSimpleName;
    char *buffer,
         *pBegin,
         *pEnd;

    USHORT usForeColor,
           usBackColor;

    KeywordAndColor *pCurKeyColor,
                    *pNextKeyColor;

    HANDLE hFile;
    WIN32_FIND_DATA wfdInfo;
    DWORD dwBytesRead;

    // Locate the specified file somewhere in the path
    if ( !SearchPath( NULL,
                      szFileName,
                      NULL,
                      _MAX_PATH,
                      szPathName,
                      &szSimpleName ) )
    {
        fprintf( stderr, "Error locating keyword/color file \"%s\"!\n",
                 szFileName );
        return;
    }

    // Get the size of the file so we can read all of it in
    hFile = FindFirstFile( szPathName, &wfdInfo );
    if ( INVALID_HANDLE_VALUE == hFile )
    {
        fprintf( stderr, "Error locating keyword/color file \"%s\"!\n",
                 szPathName );
        return;
    }
    FindClose( hFile );
    hFile = INVALID_HANDLE_VALUE;

    if ( wfdInfo.nFileSizeLow < 5 ||
         wfdInfo.nFileSizeHigh )
    {
        fprintf( stderr, "Invalid keyword/color file: %s!\n",
                 szPathName );
        return;
    }

    // Allocate memory to store file contents
    buffer = malloc( wfdInfo.nFileSizeLow );
    if ( NULL == buffer )
    {
        fprintf( stderr, "Error!  Unable to allocate memory to read in keyword/color file!\n" );
        return;
    }

    // Attempt to open the given file-name
    hFile = CreateFile( szPathName,
                        GENERIC_READ,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_FLAG_SEQUENTIAL_SCAN,
                        NULL );
    if ( INVALID_HANDLE_VALUE == hFile )
    {
        fprintf( stderr, "Error opening keyword/color file %s!\n",
                 szPathName );
        return;
    }

    // Attempt to read in the contents of the file
    ReadFile( hFile,
              buffer,
              wfdInfo.nFileSizeLow,
              &dwBytesRead,
              NULL );
    CloseHandle( hFile );

    if ( dwBytesRead != wfdInfo.nFileSizeLow )
    {
        fprintf( stderr, "Error reading keyword/color file: %s!\n",
                 szPathName );
        free( buffer );
        return;
    }

    // Parse contents of file, storing keyword(s) and color combinations
    pBegin = buffer;
    pCurKeyColor = NULL;
    while ( pBegin < buffer + dwBytesRead )
    {
        // Skip any newline/CR at beginning
        while ( pBegin < buffer + dwBytesRead &&
                ( *pBegin == '\r' ||
                  *pBegin == '\n' ) ) pBegin++;
        if ( pBegin >= buffer + dwBytesRead )
            continue;

        pEnd = pBegin;
        while ( pEnd < buffer + dwBytesRead &&
                *pEnd != '\r' ) pEnd++;
        // point at last character
        pEnd--;

        // Add new KeywordAndColor member to list
        if ( NULL == pCurKeyColor )
        {
            *ppKeyColors = pCurKeyColor = malloc( sizeof( KeywordAndColor ) );
        }
        else
        {
            pCurKeyColor->next = malloc( sizeof( KeywordAndColor ) );
            pCurKeyColor = pCurKeyColor->next;
        }

        // Verify we allocated memory for another list member
        if ( NULL == pCurKeyColor )
        {
            fprintf( stderr, "Error allocating memory for keyword/color storage!\n" );
            // Cleanup any we did create
            while ( *ppKeyColors )
            {
                pCurKeyColor = ((KeywordAndColor *)*ppKeyColors)->next;
                if ( ((KeywordAndColor *)*ppKeyColors)->szKeyword )
                    free( ((KeywordAndColor *)*ppKeyColors)->szKeyword );
                free( (KeywordAndColor *)*ppKeyColors );
                (KeywordAndColor *)*ppKeyColors = pCurKeyColor;
            }

            return;
        }

        // This is now the last member of the list
        pCurKeyColor->next = NULL;

        // Already have keyword(s) -- allocate room for it
        pCurKeyColor->szKeyword = malloc( pEnd - pBegin + 2 );
        if ( NULL == pCurKeyColor->szKeyword )
        {
            fprintf( stderr, "Error allocating memory for keyword/color storage!\n" );
            // Cleanup any we did create
            while ( *ppKeyColors )
            {
                pCurKeyColor = ((KeywordAndColor *)*ppKeyColors)->next;
                if ( ((KeywordAndColor *)*ppKeyColors)->szKeyword )
                    free( ((KeywordAndColor *)*ppKeyColors)->szKeyword );
                free( (KeywordAndColor *)*ppKeyColors );
                *ppKeyColors = pCurKeyColor;
            }

            return;
        }

        // Store keyword(s)
        memcpy( (PVOID)pCurKeyColor->szKeyword, (PVOID)pBegin, pEnd-pBegin+1 );
        pCurKeyColor->szKeyword[pEnd-pBegin+1] = '\0';

        pBegin = pEnd + 1;
        // Get color information
        if ( GetColorFromBuffer( &pBegin,
                                 (char *)(buffer + dwBytesRead),
                                 &usForeColor,
                                 FALSE ) )
        {
            // Check if there is a comma following
            while ( pBegin < buffer + dwBytesRead &&
                    *pBegin != ',' &&
                    *pBegin != '\r' ) pBegin++;
            if ( *pBegin == ',' )
            {
                pBegin++;
                if ( GetColorFromBuffer( &pBegin,
                                         (char *)(buffer + dwBytesRead),
                                         &usBackColor,
                                         TRUE ) )
                    goto noError;
            }
            else
            {
                // Default to current background color
                usBackColor = 0xffff;
                goto noError;
            }
        }
        // ERROR
        fprintf( stderr, "Invalid color information for: %s\n", pCurKeyColor->szKeyword );
        // We will leave any previous entries but delete this one
        pNextKeyColor = *ppKeyColors;
        if ( pNextKeyColor == pCurKeyColor )
        {
            free( pCurKeyColor );
            *ppKeyColors = NULL;
        }
        else
        {
            while ( pCurKeyColor != pNextKeyColor->next )
                pNextKeyColor = pNextKeyColor->next;
            free ( pCurKeyColor );
            pNextKeyColor->next = NULL;
        }
        return;

noError:
        // Store color information
        if ( usBackColor == 0xffff )
            pCurKeyColor->color = 0xfff0 |
                                  (usForeColor & 0x0f);
        else
            pCurKeyColor->color = ((usBackColor << 4) & 0x00f0) |
                                  (usForeColor & 0x0f );
    }
}

BOOL
GetColorFromBuffer(
    char **ppBuffer,
    char *pBufferInvalid,
    WORD *color,
    BOOL bStayOnLine
    )
{
    char *pBegin,
         *pEnd,
         temp;

    pBegin = *ppBuffer;
    if ( bStayOnLine )
    {
        // Skip to the next character (on this line)
        while ( pBegin < pBufferInvalid &&
                !isalnum( (int)*pBegin ) &&
                *pBegin != '\r' ) pBegin++;
    }
    else
    {
        // Skip to next character (in buffer)
        while ( pBegin < pBufferInvalid &&
                !isalnum( (int)*pBegin ) ) pBegin++;
    }

    if ( pBegin >= pBufferInvalid ||
         *pBegin == '\r' )
        return FALSE;

    // Read in color
    pEnd = pBegin + 1;
    while ( isalnum( (int)*pEnd ) &&
            *pEnd != ',' ) pEnd++;

    temp = *pEnd;
    *pEnd = '\0';
    *color = GetColorNum( pBegin );
    *pEnd = temp;

    // Use same valid color check as used for foreground/background
    if ( *color == 0xffff )
        return FALSE;

    // Move the pointer we were given to next unread portion
    *ppBuffer = pEnd;

    return TRUE;
}

BOOL
GetNextConnectInfo(
    char** SrvName,
    char** PipeName
    )
{
    char *s;

    static char szServerName[64];
    static char szPipeName[32];

    try
    {
        ZeroMemory(szServerName,64);
        ZeroMemory(szPipeName,32);
        SetConsoleTitle("Remote - Prompting for next Connection");
        printf("Debugger machine (server): ");
        fflush(stdout);

        if (((*SrvName=gets(szServerName))==NULL)||
             (strlen(szServerName)==0))
        {
            return(FALSE);
        }

        if (szServerName[0] == COMMANDCHAR &&
            (szServerName[1] == 'q' || szServerName[1] == 'Q')
           )
        {
            return(FALSE);
        }

        if (s = strchr( szServerName, ' ' )) {
            *s++ = '\0';
            while (*s == ' ') {
                s += 1;
            }
            *PipeName=strcpy(szPipeName, s);
            printf(szPipeName);
            fflush(stdout);
        }
        if (strlen(szPipeName) == 0) {
            printf("Target machine (pipe)    : ");
            fflush(stdout);
            if ((*PipeName=gets(szPipeName))==NULL)
            {
                return(FALSE);
            }
        }

        if (s = strchr(szPipeName, ' ')) {
            *s++ = '\0';
        }

        if (szPipeName[0] == COMMANDCHAR &&
            (szPipeName[1] == 'q' || szPipeName[1] == 'Q')
           )
        {
            return(FALSE);
        }
        printf("\n\n");
    }

    except(EXCEPTION_EXECUTE_HANDLER)
    {
        return(FALSE);  // Ignore exceptions
    }
    return(TRUE);
}


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

VOID
Errormsg(
    char* str
    )
{
    printf("Error (%d) - %s\n",GetLastError(),str);
}

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

BOOL
IsKdString(
    char* string
    )
{

    char* start;

    //
    // some heuristic for uninvented yet platforms
    // if the first word has "kd" in it ok
    //

    if(    ((start = strstr(string, "kd")) != NULL)
        || ((start = strstr(string, "dbg")) != NULL)
        || ((start = strstr(string, "remoteds")) != NULL)
        || ((start = strstr(string, "ntsd")) != NULL)
        || ((start = strstr(string, "cdb")) != NULL) )
    {
        // is it in the first word?
        while(--start > string)
        {
            if((*start == ' ') || (*start == '\t'))
            {
                while(--start > string)
                    if((*start != '\t') || (*start != ' '))
                        return(FALSE);
            }
        }
        return TRUE;
    }
    return(FALSE);
}


//
// WriteFileSynch is a synchronous WriteFile for overlapped
// file handles.  As a special case, two-pipe client operation
// sets fAsyncPipe FALSE and this routine then passes NULL
// for lpOverlapped.
//

BOOL
FASTCALL
WriteFileSynch(
    HANDLE  hFile,
    LPVOID  lpBuffer,
    DWORD   cbWrite,
    LPDWORD lpNumberOfBytesWritten,
    DWORD   dwFileOffset,
    LPOVERLAPPED lpO
    )
{
    BOOL Success;


    lpO->OffsetHigh = 0;
    lpO->Offset = dwFileOffset;

    Success =
        WriteFile(
            hFile,
            lpBuffer,
            cbWrite,
            lpNumberOfBytesWritten,
            fAsyncPipe ? lpO : NULL
            );

    if ( ! Success ) {

        if (ERROR_IO_PENDING == GetLastError()) {

            Success =
                GetOverlappedResult(
                    hFile,
                    lpO,
                    lpNumberOfBytesWritten,
                    TRUE
                    );
        }
    }

    return Success;
}


BOOL
FASTCALL
ReadFileSynch(
    HANDLE  hFile,
    LPVOID  lpBuffer,
    DWORD   cbRead,
    LPDWORD lpNumberOfBytesRead,
    DWORD   dwFileOffset,
    LPOVERLAPPED lpO
    )
{
    BOOL Success;

    lpO->OffsetHigh = 0;
    lpO->Offset = dwFileOffset;

    Success =
        ReadFile(
            hFile,
            lpBuffer,
            cbRead,
            lpNumberOfBytesRead,
            fAsyncPipe ? lpO : NULL
            );

    if ( ! Success ) {

        if (ERROR_IO_PENDING == GetLastError()) {

            Success =
                GetOverlappedResult(
                    hFile,
                    lpO,
                    lpNumberOfBytesRead,
                    TRUE
                    );
        }
    }

    return Success;
}

BOOL
FASTCALL
WriteConsoleWithColor(
    HANDLE MyStdOut,
    char *buffer,
    DWORD cbBuffer,
    CWCDATA *persist
    )
{
    DWORD cbWrite,
          cbFill;
    WORD color;
    BOOL bAltColor,
         bNewLine,
         bCanColor;
    char *pCurLine,
         *pEndOfLine,
         *pPrevLine,
         *pTemp;
    CONSOLE_SCREEN_BUFFER_INFO conBufferInfo;

    if ( persist->bLineContinues )
        bNewLine = FALSE;
    else
        bNewLine = TRUE;

    // Split buffer into individual lines
    pCurLine = buffer;
    while ( pCurLine < buffer + cbBuffer )
    {
        // Get console information
        bCanColor = GetConsoleScreenBufferInfo( MyStdOut, &conBufferInfo );

        // Find end of current line
        pEndOfLine = pCurLine;
        // Print out any beginning newlines/CR's -- this will avoid
        // coloring large blocks of nothing associated with keywords
        while ( pEndOfLine < buffer + cbBuffer &&
                ( *pEndOfLine == '\r' ||
                  *pEndOfLine == '\n' ) )
        {
            // New line
            if ( !bNewLine )
            {
                bNewLine = TRUE;

                // If this was a continuation line -- end it
                if ( persist->bLineContinues )
                {
                    persist->bLineContinues = FALSE;
                    // Check if we just ended a line that couldn't be parsed
                    // because of its size -- if so output warning
                    if ( persist->bLineTooLarge )
                        DisplayWarning( LINE_TOO_LONG );
                    // Otherwise check for keyword(s)
                    // and color if appropriate
                    else if ( bCanColor &&
                              pColorLine( persist->sLine,
                                          persist->cbCurPos + 1,
                                          conBufferInfo.wAttributes,
                                          &color ) )
                    {
                        // If we were unable to get the cursor position when
                        // the line started we won't be able to color it now,
                        // but because we aren't printing any warning elsewhere
                        // if we can't get console info, we will just quietly
                        // not output color here
                        if ( 0xFF != persist->cLineBegin.X ||
                             0xFF != persist->cLineBegin.Y )
                        {
                            // Color in beginning portion of line (actually all of
                            //  line up to current point gets colored to reduce
                            //  calculations)
                            FillConsoleOutputAttribute( MyStdOut,
                                                        color,
                                                        ( (conBufferInfo.dwCursorPosition.Y -
                                                           persist->cLineBegin.Y + 1) *
                                                          (conBufferInfo.srWindow.Right -
                                                           conBufferInfo.srWindow.Left) ),
                                                        persist->cLineBegin,
                                                        &cbFill );
                        }
                    }
                }
            }
            pEndOfLine++;
        }
        // Print newline characters if some were found
        if ( pEndOfLine > pCurLine )
        {
            if ( ! WriteFile(MyStdOut, pCurLine, (DWORD)(pEndOfLine - pCurLine), &cbWrite, NULL) )
            {
                // Bail out
                return FALSE;
            }

            // Move line pointer
            pCurLine = pEndOfLine;
        }

        // Get the line
        while ( pEndOfLine < buffer + cbBuffer &&
                *pEndOfLine != '\r' &&
                *pEndOfLine != '\n' ) pEndOfLine++;
        // If we got characters we are in a line
        // Check it for keywords or add it to
        // a continuation line and/or print it
        if ( pEndOfLine > pCurLine )
        {
            bNewLine = FALSE;

            // Point to last character
            pEndOfLine--;

            // Check for current console information
            if ( !bCanColor )
            {
                // Couldn't get information -- handle might
                // be redirected.  Don't change colors
                bAltColor = FALSE;
            }
            else if ( persist->bLineContinues )
            {
                // See if we have enough room to construct this new line
                if ( !persist->bLineTooLarge &&
                     (DWORD)(pEndOfLine - pCurLine + 1) >=
                     (persist->cbLine - persist->cbCurPos) )
                {
                    // Attempt to build a bigger buffer
                    pTemp = realloc( (PVOID)persist->sLine,
                                     persist->cbLine + (pEndOfLine - pCurLine + 1) );
                    if ( NULL == pTemp )
                    {
                        persist->bLineTooLarge = TRUE;
                    }
                    else
                    {
                        persist->sLine = pTemp;
                        persist->cbLine += (DWORD)(pEndOfLine - pCurLine + 1);
                    }
                }

                // Add this piece to the line
                if ( !persist->bLineTooLarge )
                {
                    // Add new piece to line
                    memcpy( (PVOID)(persist->sLine + persist->cbCurPos + 1),
                            (PVOID)pCurLine,
                            (pEndOfLine - pCurLine + 1) );
                    // Point at new end of line
                    persist->cbCurPos += (DWORD)(pEndOfLine - pCurLine + 1);
                }

                // Don't color this line portion
                bAltColor = FALSE;

            }
            // Check if line needs colored unless this is going
            // to be a continued line (last line in buffer and
            // does not end with a newline).  We do not want
            // to determine the color of the line until we
            // have the complete thing
            else if ( (char *)(pEndOfLine + 1) < (char *)(buffer + cbBuffer) )
            {
                // Parse line for keywords that will cause
                // this line to show up in a different color
                bAltColor = pColorLine( pCurLine,
                                        (DWORD)(pEndOfLine - pCurLine + 1),
                                        conBufferInfo.wAttributes,
                                        &color );
            }
            else
            {
                bAltColor = FALSE;
            }

            if ( bAltColor )
            {
                // Change color for output of this line
                SetConsoleTextAttribute( MyStdOut, color );
            }

            if ( ! WriteFile(MyStdOut, pCurLine, (DWORD)(pEndOfLine - pCurLine + 1), &cbWrite, NULL))
            {
                if ( bAltColor )
                {
                    SetConsoleTextAttribute( MyStdOut, conBufferInfo.wAttributes );
                }
                // Bail out
                return FALSE;
            }
            // Restore default colors if necessary
            if ( bAltColor )
            {
                SetConsoleTextAttribute( MyStdOut, conBufferInfo.wAttributes );
            }

            // Point to the next line, saving off this line
            // in case we need to store it in a continuation
            // line
            pPrevLine = pCurLine;
            pCurLine = pEndOfLine + 1;
        } // End only check line if there is one
    }

    // If the buffer did not end with a CR, and we are
    // not already in a continuation, remember this line
    if ( !bNewLine &&
         pPrevLine <= pEndOfLine &&
         !persist->bLineContinues )
    {
        persist->bLineContinues = TRUE;
        persist->bLineTooLarge = FALSE;

        if ( bCanColor )
            persist->cLineBegin = conBufferInfo.dwCursorPosition;
        else // Signal we were unable to obtain cursor location
        {
            persist->cLineBegin.X = 0xFF;
            persist->cLineBegin.Y = 0xFF;
        }

        // See if we have enough room to construct this new line
        if ( (DWORD)(pEndOfLine - pPrevLine + 1) >= persist->cbLine )
        {
            // Attempt to build a bigger buffer
            pTemp = realloc( (PVOID)persist->sLine,
                             persist->cbLine + (pEndOfLine - pPrevLine + 1) );
            if ( NULL == pTemp )
            {
                persist->bLineTooLarge = TRUE;
            }
            else
            {
                persist->sLine = pTemp;
                persist->cbLine = (DWORD)(pEndOfLine - pPrevLine + 1);
            }
        }

        // Store the beginning of the line
        if ( !persist->bLineTooLarge )
        {
            // Add new piece to line
            memcpy( (PVOID)persist->sLine,
                    (PVOID)pPrevLine,
                    (pEndOfLine - pPrevLine + 1) );
            // Point at new end of line
            persist->cbCurPos = (DWORD)(pEndOfLine - pPrevLine);
        }
    }

    // Success
    return TRUE;
}