904 lines
24 KiB
C
904 lines
24 KiB
C
// Copyright (c) 1998-1999 Microsoft Corporation
|
|
/******************************************************************************
|
|
*
|
|
* MSG.C
|
|
* Send a message to another user.
|
|
*
|
|
* Syntax:
|
|
*
|
|
* MSG [username] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
|
|
* MSG [WinStationName] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
|
|
* MSG [logonid] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
|
|
* MSG [@filename] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
|
|
*
|
|
* /TIME:seconds - time delay to wait for receiver to acknowledge msg\n" \
|
|
* /V - display information about actions being performed\n" \
|
|
* /W - wait for response from user, useful with /V
|
|
* /? - display syntax and options\n"
|
|
*
|
|
* Parameters:
|
|
*
|
|
* username
|
|
* Identifies all logins belonging to the specific username
|
|
*
|
|
* winstationname
|
|
* Identifies all logins connected to the winstation name regardless
|
|
* of loginname.
|
|
*
|
|
* logonid
|
|
* Decimal number specifying a logon id to send the message to
|
|
*
|
|
* @filename
|
|
* Identifies a file that contains usernames or winstation names to
|
|
* send messages to.
|
|
*
|
|
* Options:
|
|
*
|
|
* /SELF >>>> UNPUBLISHED <<<<
|
|
* Send message to caller of msg. Used to send a message to
|
|
* users when maintenace mode is enabled.
|
|
*
|
|
* /TIME:seconds (time delay)
|
|
* The amount of time to wait for an acknowledgement from the target
|
|
* login that the message has been received.
|
|
*
|
|
* /V (verbose)
|
|
* Display information about the actions being performed.
|
|
*
|
|
* /? (help)
|
|
* Display the syntax for the utility and information about the
|
|
* options.
|
|
*
|
|
* message
|
|
* The text of the message to send. If the text is not specified
|
|
* then the text is read from STDIN.
|
|
*
|
|
* Remarks:
|
|
*
|
|
* The message can be typed on the command line or be read from STDIN.
|
|
* The message is sent via a popup. The user receiving the popup can
|
|
* hit a key to get rid of it or it will go away after a default timeout.
|
|
*
|
|
* If the target of the message is a terminal, then the message is
|
|
* sent to all logins on the target terminal.
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <ntddkbd.h>
|
|
#include <ntddmou.h>
|
|
#include <winstaw.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <utilsub.h>
|
|
#include <process.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <wchar.h>
|
|
#include <io.h> // for isatty
|
|
#include <locale.h>
|
|
|
|
#include "msg.h"
|
|
#include "printfoa.h"
|
|
|
|
/*=============================================================================
|
|
== Global Data
|
|
=============================================================================*/
|
|
|
|
ULONG Seconds;
|
|
USHORT file_flag=FALSE; //wkp
|
|
USHORT v_flag;
|
|
USHORT self_flag;
|
|
USHORT help_flag;
|
|
WCHAR ids_input[MAX_IDS_LEN];
|
|
PWCHAR MsgText, MsgTitle;
|
|
WCHAR MsgLine[MAX_IDS_LEN];
|
|
ULONG gCurrentLogonId = (ULONG)(-1);
|
|
BOOLEAN wait_flag = FALSE;
|
|
HANDLE hServerName = SERVERNAME_CURRENT;
|
|
WCHAR ServerName[MAX_IDS_LEN+1];
|
|
|
|
/*
|
|
* The token map structure is used for parsing program arguments
|
|
*/
|
|
TOKMAP ptm[] = {
|
|
{ TOKEN_INPUT, TMFLAG_REQUIRED, TMFORM_STRING, MAX_IDS_LEN,
|
|
ids_input },
|
|
|
|
{ TOKEN_SERVER, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN,
|
|
ServerName},
|
|
|
|
{ TOKEN_MESSAGE, TMFLAG_OPTIONAL, TMFORM_X_STRING, MAX_IDS_LEN,
|
|
MsgLine },
|
|
|
|
{ TOKEN_TIME, TMFLAG_OPTIONAL, TMFORM_ULONG, sizeof(ULONG),
|
|
&Seconds },
|
|
|
|
{ TOKEN_VERBOSE, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
|
|
&v_flag },
|
|
|
|
{ TOKEN_WAIT, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(BOOLEAN),
|
|
&wait_flag },
|
|
|
|
{ TOKEN_SELF, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
|
|
&self_flag },
|
|
|
|
{ TOKEN_HELP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
|
|
&help_flag },
|
|
|
|
{ 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
/*
|
|
* This is the list of names we are to send the message to
|
|
*/
|
|
int NameListCount = 0;
|
|
WCHAR **NameList = NULL;
|
|
WCHAR CurrUserName[USERNAME_LENGTH];
|
|
|
|
/*
|
|
* Local function prototypes
|
|
*/
|
|
BOOLEAN SendMessageIfTarget( PLOGONID Id, ULONG Count,
|
|
LPWSTR pTitle, LPWSTR pMessage );
|
|
BOOLEAN CheckMatchList( PLOGONID );
|
|
BOOLEAN MessageSend( PLOGONID pLId, LPWSTR pTitle, LPWSTR pMessage );
|
|
BOOLEAN LoadFileToNameList( PWCHAR pName );
|
|
BOOL ReadFileByLine( HANDLE, PCHAR, DWORD, PDWORD );
|
|
void Usage( BOOLEAN bError );
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* MAIN
|
|
*
|
|
* ENTRY:
|
|
* argc - count of the command line arguments.
|
|
* argv - vector of strings containing the command line arguments.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int __cdecl
|
|
main(INT argc, CHAR **argv)
|
|
{
|
|
// struct tm * pTimeDate;
|
|
// time_t curtime;
|
|
SYSTEMTIME st;
|
|
WCHAR TimeStamp[ MAX_TIME_DATE_LEN ];
|
|
WCHAR *CmdLine;
|
|
WCHAR **argvW;
|
|
WCHAR szTitleFormat[50];
|
|
DWORD dwSize;
|
|
PLOGONID pTerm;
|
|
UINT TermCount;
|
|
ULONG Status;
|
|
int i, rc, TitleLen;
|
|
BOOLEAN MatchedOne = FALSE;
|
|
|
|
setlocale(LC_ALL, ".OCP");
|
|
|
|
/*
|
|
* Massage the command line.
|
|
*/
|
|
|
|
argvW = MassageCommandLine((DWORD)argc);
|
|
if (argvW == NULL) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
|
|
/*
|
|
* parse the cmd line without parsing the program name (argc-1, argv+1)
|
|
*/
|
|
rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
|
|
|
|
/*
|
|
* Check for error from ParseCommandLine
|
|
*/
|
|
if (rc && (rc & PARSE_FLAG_NO_PARMS) )
|
|
help_flag = TRUE;
|
|
|
|
if ( help_flag || rc ) {
|
|
if (!help_flag) {
|
|
Usage(TRUE);
|
|
return(FAILURE);
|
|
|
|
} else {
|
|
Usage(FALSE);
|
|
return(SUCCESS);
|
|
}
|
|
}
|
|
|
|
// If no remote server was specified, then check if we are running under Terminal Server
|
|
if ((!IsTokenPresent(ptm, TOKEN_SERVER) ) && (!AreWeRunningTerminalServices()))
|
|
{
|
|
ErrorPrintf(IDS_ERROR_NOT_TS);
|
|
return(FAILURE);
|
|
}
|
|
|
|
/*
|
|
* Open the specified server
|
|
*/
|
|
if( ServerName[0] ) {
|
|
hServerName = WinStationOpenServer( ServerName );
|
|
if( hServerName == NULL ) {
|
|
StringErrorPrintf(IDS_ERROR_SERVER,ServerName);
|
|
PutStdErr( GetLastError(), 0 );
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if no timeout was specified, use default
|
|
*/
|
|
if ( !IsTokenPresent(ptm, TOKEN_TIME) )
|
|
Seconds = RESPONSE_TIMEOUT;
|
|
|
|
/*
|
|
* allocate a buffer for the message header
|
|
*/
|
|
if ( (MsgText = (PWCHAR)malloc(MAX_IDS_LEN * sizeof(WCHAR))) == NULL ) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
MsgText[0] = 0;
|
|
|
|
/*
|
|
* set up message header text: sender and timestamp
|
|
*/
|
|
GetCurrentUserName(CurrUserName, USERNAME_LENGTH);
|
|
|
|
/*
|
|
* Get the current Winstation Id for this process
|
|
*/
|
|
gCurrentLogonId = GetCurrentLogonId();
|
|
|
|
/*
|
|
* Form message title string.
|
|
*/
|
|
dwSize = sizeof(szTitleFormat) / sizeof(WCHAR);
|
|
|
|
LoadString(NULL,IDS_TITLE_FORMAT,szTitleFormat,dwSize);
|
|
|
|
TitleLen = (wcslen(szTitleFormat) + wcslen(CurrUserName) + 1) * sizeof(WCHAR) + ( 2 * sizeof( TimeStamp ) );
|
|
|
|
MsgTitle = (PWCHAR)malloc(TitleLen);
|
|
|
|
if( MsgTitle == NULL )
|
|
{
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
|
|
|
|
_snwprintf(MsgTitle, TitleLen, szTitleFormat, CurrUserName);
|
|
|
|
TimeStamp[0] = 0;
|
|
|
|
GetLocalTime( &st );
|
|
|
|
GetDateFormat( LOCALE_USER_DEFAULT ,
|
|
DATE_SHORTDATE ,
|
|
&st ,
|
|
NULL ,
|
|
TimeStamp,
|
|
MAX_TIME_DATE_LEN );
|
|
|
|
|
|
wcscat(MsgTitle , TimeStamp);
|
|
|
|
TimeStamp[0] = 0;
|
|
|
|
GetTimeFormat( LOCALE_USER_DEFAULT ,
|
|
TIME_NOSECONDS ,
|
|
&st ,
|
|
NULL ,
|
|
TimeStamp,
|
|
MAX_TIME_DATE_LEN );
|
|
|
|
wcscat(MsgTitle , L" " );
|
|
wcscat(MsgTitle , TimeStamp);
|
|
|
|
/*
|
|
* if message was specified on the command line, add it to MsgText string
|
|
*/
|
|
if ( IsTokenPresent(ptm, TOKEN_MESSAGE) ) {
|
|
|
|
MsgText = realloc(MsgText, (wcslen(MsgText) + wcslen(MsgLine) + 1) * sizeof(WCHAR));
|
|
if ( MsgText == NULL ) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
wcscat(MsgText, MsgLine);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Message was not on the command line. If STDIN is connected to
|
|
* the keyboard, then prompt the user for the message to send,
|
|
* otherwise just read STDIN.
|
|
*/
|
|
|
|
if ( _isatty( _fileno(stdin) ) )
|
|
Message(IDS_MESSAGE_PROMPT);
|
|
|
|
|
|
while ( wfgets(MsgLine, MAX_IDS_LEN, stdin) != NULL ) {
|
|
MsgText = (PWCHAR)realloc(
|
|
MsgText,
|
|
(wcslen(MsgText) + wcslen(MsgLine) + 1) * sizeof(WCHAR) );
|
|
|
|
if ( MsgText == NULL ) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
wcscat(MsgText, MsgLine);
|
|
}
|
|
|
|
/*
|
|
* When we fall through, we either have an eof or a problem with
|
|
* STDIN
|
|
*/
|
|
if ( feof(stdin) ) {
|
|
|
|
/*
|
|
* If we get here then we hit eof on STDIN. First check to make
|
|
* sure that we did not get an eof on first wfgets
|
|
*/
|
|
if ( !wcslen(MsgText) ) {
|
|
ErrorPrintf(IDS_ERROR_EMPTY_MESSAGE);
|
|
return(FAILURE);
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* The return from wfgets was not eof so we have an STDIN
|
|
* problem
|
|
*/
|
|
ErrorPrintf(IDS_ERROR_STDIN_PROCESSING);
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Is the ids_input really a file indirection?
|
|
*/
|
|
if ( ids_input[0] == L'@' ) {
|
|
|
|
/*
|
|
* Open the input file and read the names into the NameList
|
|
*/
|
|
if ( !LoadFileToNameList(&ids_input[1]) )
|
|
return(FAILURE);
|
|
|
|
/*
|
|
* Ok, let's get in touch
|
|
*/
|
|
file_flag = TRUE;
|
|
|
|
} else {
|
|
|
|
_wcslwr( ids_input );
|
|
NameList = (WCHAR **)malloc( 2 * sizeof( WCHAR * ) );
|
|
if ( NameList == NULL ) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
NameList[0] = ids_input;
|
|
NameList[1] = NULL;
|
|
NameListCount = 1;
|
|
}
|
|
|
|
/*
|
|
* Enumerate across all the WinStations and send the message
|
|
* to them if there are any matches in the NameList
|
|
*/
|
|
if ( WinStationEnumerate(hServerName, &pTerm, &TermCount) ) {
|
|
|
|
if ( SendMessageIfTarget(pTerm, TermCount, MsgTitle, MsgText) )
|
|
MatchedOne = TRUE;
|
|
|
|
WinStationFreeMemory(pTerm);
|
|
|
|
} else{
|
|
|
|
Status = GetLastError();
|
|
ErrorPrintf(IDS_ERROR_WINSTATION_ENUMERATE, Status);
|
|
return(FAILURE);
|
|
}
|
|
|
|
/*
|
|
* Check for at least one match
|
|
*/
|
|
if ( !MatchedOne ) {
|
|
|
|
if( file_flag )
|
|
StringErrorPrintf(IDS_ERROR_NO_FILE_MATCHING, &ids_input[1]);
|
|
else
|
|
StringErrorPrintf(IDS_ERROR_NO_MATCHING, ids_input);
|
|
|
|
return(FAILURE);
|
|
|
|
}
|
|
|
|
return(SUCCESS);
|
|
|
|
} /* main() */
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* SendMessageIfTarget - Send a Message to a group of WinStations if
|
|
* their the target as specified by TargetName.
|
|
*
|
|
* ENTRY
|
|
* LId (input)
|
|
* Pointer to an array of LOGONIDs returned from WinStationEnumerate()
|
|
* Count (input)
|
|
* Number of elements in LOGONID array.
|
|
* pTitle (input)
|
|
* Points to message title string.
|
|
* pMessage (input)
|
|
* Points to message string.
|
|
*
|
|
* EXIT
|
|
* TRUE if message was sent to at least one WinStation; FALSE otherwise.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
SendMessageIfTarget( PLOGONID Id,
|
|
ULONG Count,
|
|
LPWSTR pTitle,
|
|
LPWSTR pMessage )
|
|
{
|
|
ULONG i;
|
|
BOOLEAN MatchedOne = FALSE;
|
|
|
|
for ( i=0; i < Count ; i++ ) {
|
|
/*
|
|
* Look at Id->WinStationName, get its User, etc. and compare
|
|
* against targetname(s). Accept '*' as "everything".
|
|
*/
|
|
if( CheckMatchList( Id ) )
|
|
{
|
|
MatchedOne = TRUE;
|
|
|
|
MessageSend(Id, pTitle, pMessage);
|
|
|
|
}
|
|
Id++;
|
|
}
|
|
return( MatchedOne );
|
|
|
|
} /* SendMessageIfTarget() */
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* CheckMatchList - Returns TRUE if the current WinStation is a match for
|
|
* sending a message due to either its name, id, or the
|
|
* name of its logged on user being in the message target(s)
|
|
* list.
|
|
*
|
|
* ENTRY
|
|
* LId (input)
|
|
* Pointer to a LOGONID returned from WinStationEnumerate()
|
|
*
|
|
* EXIT
|
|
* TRUE if this is a match, FALSE otherwise.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
CheckMatchList( PLOGONID LId )
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* Wild card matches everything
|
|
*/
|
|
if ( ids_input[0] == L'*' ) {
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* Loop through name list to see if any given name applies to
|
|
* this WinStation
|
|
*/
|
|
for( i=0; i<NameListCount; i++ ) {
|
|
if (WinStationObjectMatch( hServerName , LId, NameList[i]) ) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* MessageSend - Send a message to the target WinStation
|
|
*
|
|
* ENTRY
|
|
* LId (input)
|
|
* Pointer to a LOGONID returned from WinStationEnumerate()
|
|
* pTitle (input)
|
|
* Points to message title string.
|
|
* pMessage (input)
|
|
* Points to message string.
|
|
*
|
|
* EXIT
|
|
* TRUE message is sent, FALSE otherwise.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
MessageSend( PLOGONID LId,
|
|
LPWSTR pTitle,
|
|
LPWSTR pMessage )
|
|
{
|
|
ULONG idResponse, ReturnLength;
|
|
WINSTATIONINFORMATION WSInfo;
|
|
|
|
/*
|
|
* Make sure that the WinStation is in the 'connected' state
|
|
*/
|
|
if ( !WinStationQueryInformation( hServerName,
|
|
LId->LogonId,
|
|
WinStationInformation,
|
|
&WSInfo,
|
|
sizeof(WSInfo),
|
|
&ReturnLength ) ) {
|
|
goto BadQuery;
|
|
}
|
|
|
|
if ( WSInfo.ConnectState != State_Connected &&
|
|
WSInfo.ConnectState != State_Active ) {
|
|
goto NotConnected;
|
|
}
|
|
|
|
/*
|
|
* Send message.
|
|
*/
|
|
if ( v_flag ) {
|
|
if( LId->WinStationName[0] )
|
|
StringDwordMessage(IDS_MESSAGE_WS, LId->WinStationName, Seconds);
|
|
else
|
|
Message(IDS_MESSAGE_ID, LId->LogonId, Seconds);
|
|
|
|
}
|
|
|
|
if ( !WinStationSendMessage( hServerName,
|
|
LId->LogonId,
|
|
pTitle,
|
|
(wcslen(pTitle))*sizeof(WCHAR),
|
|
pMessage,
|
|
(wcslen(pMessage))*sizeof(WCHAR),
|
|
MB_OK, // MessageBox() Style
|
|
Seconds,
|
|
&idResponse,
|
|
(BOOLEAN)(!wait_flag) ) ) {
|
|
|
|
if( LId->WinStationName[0] )
|
|
StringDwordErrorPrintf(IDS_ERROR_MESSAGE_WS, LId->WinStationName, GetLastError() );
|
|
else
|
|
ErrorPrintf(IDS_ERROR_MESSAGE_ID, LId->LogonId, GetLastError() );
|
|
|
|
PutStdErr(GetLastError(), 0);
|
|
goto BadMessage;
|
|
}
|
|
|
|
/*
|
|
* Output response result if verbose mode.
|
|
*/
|
|
if( v_flag ) {
|
|
switch( idResponse ) {
|
|
|
|
case IDTIMEOUT:
|
|
if( LId->WinStationName[0] )
|
|
StringMessage(IDS_MESSAGE_RESPONSE_TIMEOUT_WS,
|
|
LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_TIMEOUT_ID, LId->LogonId);
|
|
|
|
break;
|
|
|
|
case IDASYNC:
|
|
if( LId->WinStationName[0] )
|
|
StringMessage(IDS_MESSAGE_RESPONSE_ASYNC_WS,
|
|
LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_ASYNC_ID, LId->LogonId);
|
|
break;
|
|
|
|
case IDCOUNTEXCEEDED:
|
|
if( LId->WinStationName[0] )
|
|
StringMessage(IDS_MESSAGE_RESPONSE_COUNT_EXCEEDED_WS,
|
|
LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_COUNT_EXCEEDED_ID,
|
|
LId->LogonId);
|
|
break;
|
|
|
|
case IDDESKTOPERROR:
|
|
if( LId->WinStationName[0] )
|
|
StringMessage(IDS_MESSAGE_RESPONSE_DESKTOP_ERROR_WS,
|
|
LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_DESKTOP_ERROR_ID,
|
|
LId->LogonId);
|
|
break;
|
|
|
|
case IDERROR:
|
|
if( LId->WinStationName[0] )
|
|
StringMessage(IDS_MESSAGE_RESPONSE_ERROR_WS,
|
|
LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_ERROR_ID,
|
|
LId->LogonId);
|
|
break;
|
|
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
if( LId->WinStationName[0] )
|
|
StringMessage(IDS_MESSAGE_RESPONSE_WS,
|
|
LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_ID,
|
|
LId->LogonId);
|
|
break;
|
|
|
|
default:
|
|
if( LId->WinStationName[0] )
|
|
DwordStringMessage(IDS_MESSAGE_RESPONSE_WS,
|
|
idResponse, LId->WinStationName);
|
|
else
|
|
Message(IDS_MESSAGE_RESPONSE_ID,
|
|
idResponse, LId->LogonId);
|
|
break;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
|
|
/*-------------------------------------
|
|
* Error cleanup and return
|
|
*/
|
|
BadMessage:
|
|
NotConnected:
|
|
BadQuery:
|
|
return(FALSE);
|
|
|
|
} /* MessageSend() */
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* LoadFileToNameList
|
|
*
|
|
* Load names from a file into the input name list.
|
|
*
|
|
* ENTRY:
|
|
* pName Name of the file to load from
|
|
*
|
|
* EXIT:
|
|
* TRUE for sucessful name load from file; FALSE if error.
|
|
*
|
|
* An appropriate error message will have been displayed on error.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
LoadFileToNameList( PWCHAR pName )
|
|
{
|
|
HANDLE hFile;
|
|
INT CurrentSize;
|
|
|
|
/*
|
|
* Open input file.
|
|
*/
|
|
|
|
hFile = CreateFile(
|
|
pName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
StringErrorPrintf(IDS_ERROR_CANT_OPEN_INPUT_FILE, pName);
|
|
PutStdErr(GetLastError(), 0);
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Allocate a large array for the name string pointers
|
|
*/
|
|
|
|
CurrentSize = 100;
|
|
if ( !(NameList = (WCHAR **)malloc(CurrentSize * sizeof(WCHAR *))) ) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
|
|
NameListCount = 0;
|
|
while( 1 ) {
|
|
BOOL fRet;
|
|
CHAR *pBuffer;
|
|
DWORD nBytesRead;
|
|
WCHAR *pwBuffer;
|
|
|
|
/*
|
|
* See if we need to grow the list
|
|
*/
|
|
|
|
if( NameListCount == CurrentSize ) {
|
|
|
|
if (!(NameList = (WCHAR **)realloc(NameList, CurrentSize+100))) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
CurrentSize += 100;
|
|
}
|
|
|
|
pBuffer = (CHAR *)LocalAlloc(LPTR, USERNAME_LENGTH * sizeof(CHAR));
|
|
if (pBuffer == NULL) {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
|
|
fRet = ReadFileByLine(
|
|
hFile,
|
|
pBuffer,
|
|
USERNAME_LENGTH,
|
|
&nBytesRead
|
|
);
|
|
if (fRet && (nBytesRead > 0)) {
|
|
INT cWChar;
|
|
|
|
cWChar = MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
pBuffer,
|
|
-1,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
pwBuffer = (WCHAR *)LocalAlloc(LPTR, (cWChar + 1) * sizeof(WCHAR));
|
|
if (pwBuffer != NULL) {
|
|
MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
pBuffer,
|
|
-1,
|
|
pwBuffer,
|
|
cWChar
|
|
);
|
|
} else {
|
|
ErrorPrintf(IDS_ERROR_MALLOC);
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (pwBuffer[wcslen(pwBuffer)-1] == L'\n') {
|
|
pwBuffer[wcslen(pwBuffer)-1] = (WCHAR)NULL;
|
|
}
|
|
|
|
_wcslwr(pwBuffer);
|
|
NameList[NameListCount++] = pwBuffer;
|
|
} else {
|
|
NameList[NameListCount] = NULL;
|
|
CloseHandle(hFile);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
} /* LoadFileToNameList() */
|
|
|
|
BOOL
|
|
ReadFileByLine(
|
|
HANDLE hFile,
|
|
PCHAR pBuffer,
|
|
DWORD cbBuffer,
|
|
PDWORD pcbBytesRead
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
|
|
fRet = ReadFile(
|
|
hFile,
|
|
pBuffer,
|
|
cbBuffer - 1,
|
|
pcbBytesRead,
|
|
NULL
|
|
);
|
|
if (fRet && (*pcbBytesRead > 0)) {
|
|
CHAR* pNewLine;
|
|
|
|
pNewLine = strstr(pBuffer, "\r\n");
|
|
if (pNewLine != NULL) {
|
|
LONG lOffset;
|
|
|
|
lOffset = (LONG)(pNewLine + 2 - pBuffer) - (*pcbBytesRead);
|
|
if (SetFilePointer(hFile, lOffset, NULL, FILE_CURRENT) ==
|
|
0xFFFFFFFF) {
|
|
return(FALSE);
|
|
}
|
|
|
|
*pNewLine = (CHAR)NULL;
|
|
}
|
|
|
|
}
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Usage
|
|
*
|
|
* Output the usage message for this utility.
|
|
*
|
|
* ENTRY:
|
|
* bError (input)
|
|
* TRUE if the 'invalid parameter(s)' message should preceed the usage
|
|
* message and the output go to stderr; FALSE for no such error
|
|
* string and output goes to stdout.
|
|
*
|
|
* EXIT:
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
Usage( BOOLEAN bError )
|
|
{
|
|
if ( bError ) {
|
|
ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
|
|
ErrorPrintf(IDS_USAGE1);
|
|
ErrorPrintf(IDS_USAGE2);
|
|
ErrorPrintf(IDS_USAGE3);
|
|
ErrorPrintf(IDS_USAGE4);
|
|
ErrorPrintf(IDS_USAGE5);
|
|
ErrorPrintf(IDS_USAGE6);
|
|
ErrorPrintf(IDS_USAGE7);
|
|
ErrorPrintf(IDS_USAGE8);
|
|
ErrorPrintf(IDS_USAGE9);
|
|
ErrorPrintf(IDS_USAGEA);
|
|
ErrorPrintf(IDS_USAGEB);
|
|
ErrorPrintf(IDS_USAGEC);
|
|
ErrorPrintf(IDS_USAGED);
|
|
ErrorPrintf(IDS_USAGEE);
|
|
ErrorPrintf(IDS_USAGEF);
|
|
}
|
|
else
|
|
{
|
|
Message(IDS_USAGE1);
|
|
Message(IDS_USAGE2);
|
|
Message(IDS_USAGE3);
|
|
Message(IDS_USAGE4);
|
|
Message(IDS_USAGE5);
|
|
Message(IDS_USAGE6);
|
|
Message(IDS_USAGE7);
|
|
Message(IDS_USAGE8);
|
|
Message(IDS_USAGE9);
|
|
Message(IDS_USAGEA);
|
|
Message(IDS_USAGEB);
|
|
Message(IDS_USAGEC);
|
|
Message(IDS_USAGED);
|
|
Message(IDS_USAGEE);
|
|
Message(IDS_USAGEF);
|
|
}
|
|
} /* Usage() */
|
|
|