1517 lines
42 KiB
C
1517 lines
42 KiB
C
|
|
/******************************************************************************\
|
|
* 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 <precomp.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 ;
|
|
BOOL SaveDaclToRegistry ;
|
|
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;
|
|
|
|
const 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 ;
|
|
int rc=1;
|
|
|
|
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++;
|
|
ZeroMemory(sTitle, sizeof(sTitle));
|
|
_snprintf(sTitle,sizeof(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;
|
|
}
|
|
ZeroMemory(sTitle, sizeof(sTitle));
|
|
strncpy(sTitle,argv[i], sizeof(sTitle)/sizeof(sTitle[0])-1);
|
|
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 if ( (argv[i][2] == 's' ) ||
|
|
(argv[i][2] == 'S' ) )
|
|
{
|
|
SaveDaclToRegistry = TRUE ;
|
|
break;
|
|
|
|
}
|
|
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))
|
|
)
|
|
{
|
|
ZeroMemory(sTitle, sizeof(sTitle));
|
|
_snprintf(sTitle,sizeof(sTitle), "Remote /C %s \"%s\"",ServerName,PipeName);
|
|
SetConsoleTitle(sTitle);
|
|
|
|
//
|
|
// Start Client (Client.C)
|
|
//
|
|
rc = Client(ServerName,PipeName);
|
|
}
|
|
done = !bPromptForArgs || !gotinfo;
|
|
}
|
|
}
|
|
|
|
if (RunType==REMOTE_SERVER)
|
|
{
|
|
if (privacy == PRIVACY_VISIBLE ||
|
|
(privacy == PRIVACY_DEFAULT && IsKdString(ChildCmd))) {
|
|
|
|
strncat(sTitle, " visible", sizeof(sTitle) - strlen(sTitle) - 1);
|
|
IsAdvertise = TRUE;
|
|
}
|
|
|
|
SetConsoleTitle(sTitle);
|
|
|
|
rc = OverlappedServer(ChildCmd, PipeName);
|
|
}
|
|
|
|
//
|
|
//Reset Colors
|
|
//
|
|
SetColor(csbiOriginal.wAttributes);
|
|
if (orgTitle[0]) {
|
|
SetConsoleTitle(orgTitle);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*************************************************************/
|
|
VOID
|
|
ErrorExit(
|
|
char* str
|
|
)
|
|
{
|
|
extern PSZ pszPipeName;
|
|
DWORD dwErr;
|
|
|
|
dwErr = GetLastError();
|
|
|
|
printf("REMOTE error %d: %s\n", dwErr, str);
|
|
|
|
#if DBG
|
|
{
|
|
char szMsg[1024];
|
|
|
|
_snprintf(szMsg, sizeof(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:
|
|
fputs( "\n[REMOTE: WARNING: LINE TOO LONG TO PARSE FOR COLOR KEYWORDS]\n", stdout );
|
|
break;
|
|
default:
|
|
fputs( "\n[REMOTE: WARNING: UNSPECIFIED PROBLEM COLORING LINE]\n", stdout );
|
|
}
|
|
}
|
|
|
|
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 )
|
|
{
|
|
fputs( "Error! Unable to allocate memory to read in keyword/color file!\n", stderr );
|
|
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
|
|
if (!ReadFile( hFile, buffer, wfdInfo.nFileSizeLow, &dwBytesRead, NULL ))
|
|
{
|
|
fprintf( stderr, "Error reading keyword/color file: %s!\n", szPathName );
|
|
free( buffer );
|
|
return;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
fputs( "Error allocating memory for keyword/color storage!\n", stderr );
|
|
// 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 )
|
|
{
|
|
fputs( "Error allocating memory for keyword/color storage!\n", stderr );
|
|
// 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];
|
|
int StringLen;
|
|
|
|
try
|
|
{
|
|
ZeroMemory(szServerName,64);
|
|
ZeroMemory(szPipeName,32);
|
|
SetConsoleTitle("Remote - Prompting for next Connection");
|
|
fputs("Debugger machine (server): ", stdout);
|
|
fflush(stdout);
|
|
|
|
if (!fgets(szServerName, sizeof(szServerName), stdin)) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringLen = strlen(szServerName);
|
|
|
|
if (!StringLen || (!feof(stdin) && szServerName[StringLen-1] != '\n')) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (szServerName[StringLen-1] == '\n') {
|
|
if (StringLen == 1) {
|
|
return (FALSE);
|
|
}
|
|
szServerName[StringLen-1] = '\0';
|
|
}
|
|
|
|
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);
|
|
fputs(szPipeName, stdout);
|
|
fflush(stdout);
|
|
}
|
|
if (strlen(szPipeName) == 0) {
|
|
fputs("Target machine (pipe) : ", stdout);
|
|
fflush(stdout);
|
|
|
|
if (!fgets(szPipeName, sizeof(szPipeName), stdin)) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringLen = strlen(szPipeName);
|
|
|
|
if (!StringLen || (!feof(stdin) && szPipeName[StringLen-1] != '\n')) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (szPipeName[StringLen-1] == '\n') {
|
|
szPipeName[StringLen-1] = '\0';
|
|
}
|
|
}
|
|
|
|
if (s = strchr(szPipeName, ' ')) {
|
|
*s++ = '\0';
|
|
}
|
|
|
|
if (szPipeName[0] == COMMANDCHAR &&
|
|
(szPipeName[1] == 'q' || szPipeName[1] == 'Q')
|
|
)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
fputs("\n\n", stdout);
|
|
}
|
|
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return(FALSE); // Ignore exceptions
|
|
}
|
|
|
|
*SrvName = szServerName;
|
|
*PipeName = szPipeName;
|
|
|
|
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 = FALSE;
|
|
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;
|
|
}
|