Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

903 lines
24 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. /******************************************************************************
  3. *
  4. * MSG.C
  5. * Send a message to another user.
  6. *
  7. * Syntax:
  8. *
  9. * MSG [username] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  10. * MSG [WinStationName] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  11. * MSG [logonid] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  12. * MSG [@filename] [/TIME:seconds] [/V] [/W] [/?] [message]\n" \
  13. *
  14. * /TIME:seconds - time delay to wait for receiver to acknowledge msg\n" \
  15. * /V - display information about actions being performed\n" \
  16. * /W - wait for response from user, useful with /V
  17. * /? - display syntax and options\n"
  18. *
  19. * Parameters:
  20. *
  21. * username
  22. * Identifies all logins belonging to the specific username
  23. *
  24. * winstationname
  25. * Identifies all logins connected to the winstation name regardless
  26. * of loginname.
  27. *
  28. * logonid
  29. * Decimal number specifying a logon id to send the message to
  30. *
  31. * @filename
  32. * Identifies a file that contains usernames or winstation names to
  33. * send messages to.
  34. *
  35. * Options:
  36. *
  37. * /SELF >>>> UNPUBLISHED <<<<
  38. * Send message to caller of msg. Used to send a message to
  39. * users when maintenace mode is enabled.
  40. *
  41. * /TIME:seconds (time delay)
  42. * The amount of time to wait for an acknowledgement from the target
  43. * login that the message has been received.
  44. *
  45. * /V (verbose)
  46. * Display information about the actions being performed.
  47. *
  48. * /? (help)
  49. * Display the syntax for the utility and information about the
  50. * options.
  51. *
  52. * message
  53. * The text of the message to send. If the text is not specified
  54. * then the text is read from STDIN.
  55. *
  56. * Remarks:
  57. *
  58. * The message can be typed on the command line or be read from STDIN.
  59. * The message is sent via a popup. The user receiving the popup can
  60. * hit a key to get rid of it or it will go away after a default timeout.
  61. *
  62. * If the target of the message is a terminal, then the message is
  63. * sent to all logins on the target terminal.
  64. *
  65. *
  66. *******************************************************************************/
  67. #include <nt.h>
  68. #include <ntrtl.h>
  69. #include <nturtl.h>
  70. #include <stdio.h>
  71. #include <windows.h>
  72. #include <ntddkbd.h>
  73. #include <ntddmou.h>
  74. #include <winstaw.h>
  75. #include <stdlib.h>
  76. #include <time.h>
  77. #include <utilsub.h>
  78. #include <process.h>
  79. #include <string.h>
  80. #include <malloc.h>
  81. #include <wchar.h>
  82. #include <io.h> // for isatty
  83. #include <locale.h>
  84. #include "msg.h"
  85. #include "printfoa.h"
  86. /*=============================================================================
  87. == Global Data
  88. =============================================================================*/
  89. ULONG Seconds;
  90. USHORT file_flag=FALSE; //wkp
  91. USHORT v_flag;
  92. USHORT self_flag;
  93. USHORT help_flag;
  94. WCHAR ids_input[MAX_IDS_LEN];
  95. PWCHAR MsgText, MsgTitle;
  96. WCHAR MsgLine[MAX_IDS_LEN];
  97. ULONG gCurrentLogonId = (ULONG)(-1);
  98. BOOLEAN wait_flag = FALSE;
  99. HANDLE hServerName = SERVERNAME_CURRENT;
  100. WCHAR ServerName[MAX_IDS_LEN+1];
  101. /*
  102. * The token map structure is used for parsing program arguments
  103. */
  104. TOKMAP ptm[] = {
  105. { TOKEN_INPUT, TMFLAG_REQUIRED, TMFORM_STRING, MAX_IDS_LEN,
  106. ids_input },
  107. { TOKEN_SERVER, TMFLAG_OPTIONAL, TMFORM_STRING, MAX_IDS_LEN,
  108. ServerName},
  109. { TOKEN_MESSAGE, TMFLAG_OPTIONAL, TMFORM_X_STRING, MAX_IDS_LEN,
  110. MsgLine },
  111. { TOKEN_TIME, TMFLAG_OPTIONAL, TMFORM_ULONG, sizeof(ULONG),
  112. &Seconds },
  113. { TOKEN_VERBOSE, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
  114. &v_flag },
  115. { TOKEN_WAIT, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(BOOLEAN),
  116. &wait_flag },
  117. { TOKEN_SELF, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
  118. &self_flag },
  119. { TOKEN_HELP, TMFLAG_OPTIONAL, TMFORM_BOOLEAN, sizeof(USHORT),
  120. &help_flag },
  121. { 0, 0, 0, 0, 0}
  122. };
  123. /*
  124. * This is the list of names we are to send the message to
  125. */
  126. int NameListCount = 0;
  127. WCHAR **NameList = NULL;
  128. WCHAR CurrUserName[USERNAME_LENGTH];
  129. /*
  130. * Local function prototypes
  131. */
  132. BOOLEAN SendMessageIfTarget( PLOGONID Id, ULONG Count,
  133. LPWSTR pTitle, LPWSTR pMessage );
  134. BOOLEAN CheckMatchList( PLOGONID );
  135. BOOLEAN MessageSend( PLOGONID pLId, LPWSTR pTitle, LPWSTR pMessage );
  136. BOOLEAN LoadFileToNameList( PWCHAR pName );
  137. BOOL ReadFileByLine( HANDLE, PCHAR, DWORD, PDWORD );
  138. void Usage( BOOLEAN bError );
  139. /*****************************************************************************
  140. *
  141. * MAIN
  142. *
  143. * ENTRY:
  144. * argc - count of the command line arguments.
  145. * argv - vector of strings containing the command line arguments.
  146. *
  147. ****************************************************************************/
  148. int __cdecl
  149. main(INT argc, CHAR **argv)
  150. {
  151. // struct tm * pTimeDate;
  152. // time_t curtime;
  153. SYSTEMTIME st;
  154. WCHAR TimeStamp[ MAX_TIME_DATE_LEN ];
  155. WCHAR *CmdLine;
  156. WCHAR **argvW;
  157. WCHAR szTitleFormat[50];
  158. DWORD dwSize;
  159. PLOGONID pTerm;
  160. UINT TermCount;
  161. ULONG Status;
  162. int i, rc, TitleLen;
  163. BOOLEAN MatchedOne = FALSE;
  164. setlocale(LC_ALL, ".OCP");
  165. /*
  166. * Massage the command line.
  167. */
  168. argvW = MassageCommandLine((DWORD)argc);
  169. if (argvW == NULL) {
  170. ErrorPrintf(IDS_ERROR_MALLOC);
  171. return(FAILURE);
  172. }
  173. /*
  174. * parse the cmd line without parsing the program name (argc-1, argv+1)
  175. */
  176. rc = ParseCommandLine(argc-1, argvW+1, ptm, 0);
  177. /*
  178. * Check for error from ParseCommandLine
  179. */
  180. if (rc && (rc & PARSE_FLAG_NO_PARMS) )
  181. help_flag = TRUE;
  182. if ( help_flag || rc ) {
  183. if (!help_flag) {
  184. Usage(TRUE);
  185. return(FAILURE);
  186. } else {
  187. Usage(FALSE);
  188. return(SUCCESS);
  189. }
  190. }
  191. // If no remote server was specified, then check if we are running under Terminal Server
  192. if ((!IsTokenPresent(ptm, TOKEN_SERVER) ) && (!AreWeRunningTerminalServices()))
  193. {
  194. ErrorPrintf(IDS_ERROR_NOT_TS);
  195. return(FAILURE);
  196. }
  197. /*
  198. * Open the specified server
  199. */
  200. if( ServerName[0] ) {
  201. hServerName = WinStationOpenServer( ServerName );
  202. if( hServerName == NULL ) {
  203. StringErrorPrintf(IDS_ERROR_SERVER,ServerName);
  204. PutStdErr( GetLastError(), 0 );
  205. return(FAILURE);
  206. }
  207. }
  208. /*
  209. * if no timeout was specified, use default
  210. */
  211. if ( !IsTokenPresent(ptm, TOKEN_TIME) )
  212. Seconds = RESPONSE_TIMEOUT;
  213. /*
  214. * allocate a buffer for the message header
  215. */
  216. if ( (MsgText = (PWCHAR)malloc(MAX_IDS_LEN * sizeof(WCHAR))) == NULL ) {
  217. ErrorPrintf(IDS_ERROR_MALLOC);
  218. return(FAILURE);
  219. }
  220. MsgText[0] = 0;
  221. /*
  222. * set up message header text: sender and timestamp
  223. */
  224. GetCurrentUserName(CurrUserName, USERNAME_LENGTH);
  225. /*
  226. * Get the current Winstation Id for this process
  227. */
  228. gCurrentLogonId = GetCurrentLogonId();
  229. /*
  230. * Form message title string.
  231. */
  232. dwSize = sizeof(szTitleFormat) / sizeof(WCHAR);
  233. LoadString(NULL,IDS_TITLE_FORMAT,szTitleFormat,dwSize);
  234. TitleLen = (wcslen(szTitleFormat) + wcslen(CurrUserName) + 1) * sizeof(WCHAR) + ( 2 * sizeof( TimeStamp ) );
  235. MsgTitle = (PWCHAR)malloc(TitleLen);
  236. if( MsgTitle == NULL )
  237. {
  238. ErrorPrintf(IDS_ERROR_MALLOC);
  239. return(FAILURE);
  240. }
  241. _snwprintf(MsgTitle, TitleLen, szTitleFormat, CurrUserName);
  242. TimeStamp[0] = 0;
  243. GetLocalTime( &st );
  244. GetDateFormat( LOCALE_USER_DEFAULT ,
  245. DATE_SHORTDATE ,
  246. &st ,
  247. NULL ,
  248. TimeStamp,
  249. MAX_TIME_DATE_LEN );
  250. wcscat(MsgTitle , TimeStamp);
  251. TimeStamp[0] = 0;
  252. GetTimeFormat( LOCALE_USER_DEFAULT ,
  253. TIME_NOSECONDS ,
  254. &st ,
  255. NULL ,
  256. TimeStamp,
  257. MAX_TIME_DATE_LEN );
  258. wcscat(MsgTitle , L" " );
  259. wcscat(MsgTitle , TimeStamp);
  260. /*
  261. * if message was specified on the command line, add it to MsgText string
  262. */
  263. if ( IsTokenPresent(ptm, TOKEN_MESSAGE) ) {
  264. MsgText = realloc(MsgText, (wcslen(MsgText) + wcslen(MsgLine) + 1) * sizeof(WCHAR));
  265. if ( MsgText == NULL ) {
  266. ErrorPrintf(IDS_ERROR_MALLOC);
  267. return(FAILURE);
  268. }
  269. wcscat(MsgText, MsgLine);
  270. } else {
  271. /*
  272. * Message was not on the command line. If STDIN is connected to
  273. * the keyboard, then prompt the user for the message to send,
  274. * otherwise just read STDIN.
  275. */
  276. if ( _isatty( _fileno(stdin) ) )
  277. Message(IDS_MESSAGE_PROMPT);
  278. while ( wfgets(MsgLine, MAX_IDS_LEN, stdin) != NULL ) {
  279. MsgText = (PWCHAR)realloc(
  280. MsgText,
  281. (wcslen(MsgText) + wcslen(MsgLine) + 1) * sizeof(WCHAR) );
  282. if ( MsgText == NULL ) {
  283. ErrorPrintf(IDS_ERROR_MALLOC);
  284. return(FAILURE);
  285. }
  286. wcscat(MsgText, MsgLine);
  287. }
  288. /*
  289. * When we fall through, we either have an eof or a problem with
  290. * STDIN
  291. */
  292. if ( feof(stdin) ) {
  293. /*
  294. * If we get here then we hit eof on STDIN. First check to make
  295. * sure that we did not get an eof on first wfgets
  296. */
  297. if ( !wcslen(MsgText) ) {
  298. ErrorPrintf(IDS_ERROR_EMPTY_MESSAGE);
  299. return(FAILURE);
  300. }
  301. } else {
  302. /*
  303. * The return from wfgets was not eof so we have an STDIN
  304. * problem
  305. */
  306. ErrorPrintf(IDS_ERROR_STDIN_PROCESSING);
  307. return(FAILURE);
  308. }
  309. }
  310. /*
  311. * Is the ids_input really a file indirection?
  312. */
  313. if ( ids_input[0] == L'@' ) {
  314. /*
  315. * Open the input file and read the names into the NameList
  316. */
  317. if ( !LoadFileToNameList(&ids_input[1]) )
  318. return(FAILURE);
  319. /*
  320. * Ok, let's get in touch
  321. */
  322. file_flag = TRUE;
  323. } else {
  324. _wcslwr( ids_input );
  325. NameList = (WCHAR **)malloc( 2 * sizeof( WCHAR * ) );
  326. if ( NameList == NULL ) {
  327. ErrorPrintf(IDS_ERROR_MALLOC);
  328. return(FAILURE);
  329. }
  330. NameList[0] = ids_input;
  331. NameList[1] = NULL;
  332. NameListCount = 1;
  333. }
  334. /*
  335. * Enumerate across all the WinStations and send the message
  336. * to them if there are any matches in the NameList
  337. */
  338. if ( WinStationEnumerate(hServerName, &pTerm, &TermCount) ) {
  339. if ( SendMessageIfTarget(pTerm, TermCount, MsgTitle, MsgText) )
  340. MatchedOne = TRUE;
  341. WinStationFreeMemory(pTerm);
  342. } else{
  343. Status = GetLastError();
  344. ErrorPrintf(IDS_ERROR_WINSTATION_ENUMERATE, Status);
  345. return(FAILURE);
  346. }
  347. /*
  348. * Check for at least one match
  349. */
  350. if ( !MatchedOne ) {
  351. if( file_flag )
  352. StringErrorPrintf(IDS_ERROR_NO_FILE_MATCHING, &ids_input[1]);
  353. else
  354. StringErrorPrintf(IDS_ERROR_NO_MATCHING, ids_input);
  355. return(FAILURE);
  356. }
  357. return(SUCCESS);
  358. } /* main() */
  359. /******************************************************************************
  360. *
  361. * SendMessageIfTarget - Send a Message to a group of WinStations if
  362. * their the target as specified by TargetName.
  363. *
  364. * ENTRY
  365. * LId (input)
  366. * Pointer to an array of LOGONIDs returned from WinStationEnumerate()
  367. * Count (input)
  368. * Number of elements in LOGONID array.
  369. * pTitle (input)
  370. * Points to message title string.
  371. * pMessage (input)
  372. * Points to message string.
  373. *
  374. * EXIT
  375. * TRUE if message was sent to at least one WinStation; FALSE otherwise.
  376. *
  377. *****************************************************************************/
  378. BOOLEAN
  379. SendMessageIfTarget( PLOGONID Id,
  380. ULONG Count,
  381. LPWSTR pTitle,
  382. LPWSTR pMessage )
  383. {
  384. ULONG i;
  385. BOOLEAN MatchedOne = FALSE;
  386. for ( i=0; i < Count ; i++ ) {
  387. /*
  388. * Look at Id->WinStationName, get its User, etc. and compare
  389. * against targetname(s). Accept '*' as "everything".
  390. */
  391. if( CheckMatchList( Id ) )
  392. {
  393. MatchedOne = TRUE;
  394. MessageSend(Id, pTitle, pMessage);
  395. }
  396. Id++;
  397. }
  398. return( MatchedOne );
  399. } /* SendMessageIfTarget() */
  400. /******************************************************************************
  401. *
  402. * CheckMatchList - Returns TRUE if the current WinStation is a match for
  403. * sending a message due to either its name, id, or the
  404. * name of its logged on user being in the message target(s)
  405. * list.
  406. *
  407. * ENTRY
  408. * LId (input)
  409. * Pointer to a LOGONID returned from WinStationEnumerate()
  410. *
  411. * EXIT
  412. * TRUE if this is a match, FALSE otherwise.
  413. *
  414. *****************************************************************************/
  415. BOOLEAN
  416. CheckMatchList( PLOGONID LId )
  417. {
  418. int i;
  419. /*
  420. * Wild card matches everything
  421. */
  422. if ( ids_input[0] == L'*' ) {
  423. return(TRUE);
  424. }
  425. /*
  426. * Loop through name list to see if any given name applies to
  427. * this WinStation
  428. */
  429. for( i=0; i<NameListCount; i++ ) {
  430. if (WinStationObjectMatch( hServerName , LId, NameList[i]) ) {
  431. return(TRUE);
  432. }
  433. }
  434. return(FALSE);
  435. }
  436. /******************************************************************************
  437. *
  438. * MessageSend - Send a message to the target WinStation
  439. *
  440. * ENTRY
  441. * LId (input)
  442. * Pointer to a LOGONID returned from WinStationEnumerate()
  443. * pTitle (input)
  444. * Points to message title string.
  445. * pMessage (input)
  446. * Points to message string.
  447. *
  448. * EXIT
  449. * TRUE message is sent, FALSE otherwise.
  450. *
  451. *****************************************************************************/
  452. BOOLEAN
  453. MessageSend( PLOGONID LId,
  454. LPWSTR pTitle,
  455. LPWSTR pMessage )
  456. {
  457. ULONG idResponse, ReturnLength;
  458. WINSTATIONINFORMATION WSInfo;
  459. /*
  460. * Make sure that the WinStation is in the 'connected' state
  461. */
  462. if ( !WinStationQueryInformation( hServerName,
  463. LId->LogonId,
  464. WinStationInformation,
  465. &WSInfo,
  466. sizeof(WSInfo),
  467. &ReturnLength ) ) {
  468. goto BadQuery;
  469. }
  470. if ( WSInfo.ConnectState != State_Connected &&
  471. WSInfo.ConnectState != State_Active ) {
  472. goto NotConnected;
  473. }
  474. /*
  475. * Send message.
  476. */
  477. if ( v_flag ) {
  478. if( LId->WinStationName[0] )
  479. StringDwordMessage(IDS_MESSAGE_WS, LId->WinStationName, Seconds);
  480. else
  481. Message(IDS_MESSAGE_ID, LId->LogonId, Seconds);
  482. }
  483. if ( !WinStationSendMessage( hServerName,
  484. LId->LogonId,
  485. pTitle,
  486. (wcslen(pTitle))*sizeof(WCHAR),
  487. pMessage,
  488. (wcslen(pMessage))*sizeof(WCHAR),
  489. MB_OK, // MessageBox() Style
  490. Seconds,
  491. &idResponse,
  492. (BOOLEAN)(!wait_flag) ) ) {
  493. if( LId->WinStationName[0] )
  494. StringDwordErrorPrintf(IDS_ERROR_MESSAGE_WS, LId->WinStationName, GetLastError() );
  495. else
  496. ErrorPrintf(IDS_ERROR_MESSAGE_ID, LId->LogonId, GetLastError() );
  497. PutStdErr(GetLastError(), 0);
  498. goto BadMessage;
  499. }
  500. /*
  501. * Output response result if verbose mode.
  502. */
  503. if( v_flag ) {
  504. switch( idResponse ) {
  505. case IDTIMEOUT:
  506. if( LId->WinStationName[0] )
  507. StringMessage(IDS_MESSAGE_RESPONSE_TIMEOUT_WS,
  508. LId->WinStationName);
  509. else
  510. Message(IDS_MESSAGE_RESPONSE_TIMEOUT_ID, LId->LogonId);
  511. break;
  512. case IDASYNC:
  513. if( LId->WinStationName[0] )
  514. StringMessage(IDS_MESSAGE_RESPONSE_ASYNC_WS,
  515. LId->WinStationName);
  516. else
  517. Message(IDS_MESSAGE_RESPONSE_ASYNC_ID, LId->LogonId);
  518. break;
  519. case IDCOUNTEXCEEDED:
  520. if( LId->WinStationName[0] )
  521. StringMessage(IDS_MESSAGE_RESPONSE_COUNT_EXCEEDED_WS,
  522. LId->WinStationName);
  523. else
  524. Message(IDS_MESSAGE_RESPONSE_COUNT_EXCEEDED_ID,
  525. LId->LogonId);
  526. break;
  527. case IDDESKTOPERROR:
  528. if( LId->WinStationName[0] )
  529. StringMessage(IDS_MESSAGE_RESPONSE_DESKTOP_ERROR_WS,
  530. LId->WinStationName);
  531. else
  532. Message(IDS_MESSAGE_RESPONSE_DESKTOP_ERROR_ID,
  533. LId->LogonId);
  534. break;
  535. case IDERROR:
  536. if( LId->WinStationName[0] )
  537. StringMessage(IDS_MESSAGE_RESPONSE_ERROR_WS,
  538. LId->WinStationName);
  539. else
  540. Message(IDS_MESSAGE_RESPONSE_ERROR_ID,
  541. LId->LogonId);
  542. break;
  543. case IDOK:
  544. case IDCANCEL:
  545. if( LId->WinStationName[0] )
  546. StringMessage(IDS_MESSAGE_RESPONSE_WS,
  547. LId->WinStationName);
  548. else
  549. Message(IDS_MESSAGE_RESPONSE_ID,
  550. LId->LogonId);
  551. break;
  552. default:
  553. if( LId->WinStationName[0] )
  554. DwordStringMessage(IDS_MESSAGE_RESPONSE_WS,
  555. idResponse, LId->WinStationName);
  556. else
  557. Message(IDS_MESSAGE_RESPONSE_ID,
  558. idResponse, LId->LogonId);
  559. break;
  560. }
  561. }
  562. return(TRUE);
  563. /*-------------------------------------
  564. * Error cleanup and return
  565. */
  566. BadMessage:
  567. NotConnected:
  568. BadQuery:
  569. return(FALSE);
  570. } /* MessageSend() */
  571. /******************************************************************************
  572. *
  573. * LoadFileToNameList
  574. *
  575. * Load names from a file into the input name list.
  576. *
  577. * ENTRY:
  578. * pName Name of the file to load from
  579. *
  580. * EXIT:
  581. * TRUE for sucessful name load from file; FALSE if error.
  582. *
  583. * An appropriate error message will have been displayed on error.
  584. *
  585. *****************************************************************************/
  586. BOOLEAN
  587. LoadFileToNameList( PWCHAR pName )
  588. {
  589. HANDLE hFile;
  590. INT CurrentSize;
  591. /*
  592. * Open input file.
  593. */
  594. hFile = CreateFile(
  595. pName,
  596. GENERIC_READ,
  597. FILE_SHARE_READ | FILE_SHARE_WRITE,
  598. NULL,
  599. OPEN_EXISTING,
  600. FILE_ATTRIBUTE_NORMAL,
  601. NULL
  602. );
  603. if (hFile == INVALID_HANDLE_VALUE) {
  604. StringErrorPrintf(IDS_ERROR_CANT_OPEN_INPUT_FILE, pName);
  605. PutStdErr(GetLastError(), 0);
  606. return(FALSE);
  607. }
  608. /*
  609. * Allocate a large array for the name string pointers
  610. */
  611. CurrentSize = 100;
  612. if ( !(NameList = (WCHAR **)malloc(CurrentSize * sizeof(WCHAR *))) ) {
  613. ErrorPrintf(IDS_ERROR_MALLOC);
  614. return(FAILURE);
  615. }
  616. NameListCount = 0;
  617. while( 1 ) {
  618. BOOL fRet;
  619. CHAR *pBuffer;
  620. DWORD nBytesRead;
  621. WCHAR *pwBuffer;
  622. /*
  623. * See if we need to grow the list
  624. */
  625. if( NameListCount == CurrentSize ) {
  626. if (!(NameList = (WCHAR **)realloc(NameList, CurrentSize+100))) {
  627. ErrorPrintf(IDS_ERROR_MALLOC);
  628. return(FAILURE);
  629. }
  630. CurrentSize += 100;
  631. }
  632. pBuffer = (CHAR *)LocalAlloc(LPTR, USERNAME_LENGTH * sizeof(CHAR));
  633. if (pBuffer == NULL) {
  634. ErrorPrintf(IDS_ERROR_MALLOC);
  635. return(FAILURE);
  636. }
  637. fRet = ReadFileByLine(
  638. hFile,
  639. pBuffer,
  640. USERNAME_LENGTH,
  641. &nBytesRead
  642. );
  643. if (fRet && (nBytesRead > 0)) {
  644. INT cWChar;
  645. cWChar = MultiByteToWideChar(
  646. CP_ACP,
  647. MB_PRECOMPOSED,
  648. pBuffer,
  649. -1,
  650. NULL,
  651. 0
  652. );
  653. pwBuffer = (WCHAR *)LocalAlloc(LPTR, (cWChar + 1) * sizeof(WCHAR));
  654. if (pwBuffer != NULL) {
  655. MultiByteToWideChar(
  656. CP_ACP,
  657. MB_PRECOMPOSED,
  658. pBuffer,
  659. -1,
  660. pwBuffer,
  661. cWChar
  662. );
  663. } else {
  664. ErrorPrintf(IDS_ERROR_MALLOC);
  665. return(FAILURE);
  666. }
  667. if (pwBuffer[wcslen(pwBuffer)-1] == L'\n') {
  668. pwBuffer[wcslen(pwBuffer)-1] = (WCHAR)NULL;
  669. }
  670. _wcslwr(pwBuffer);
  671. NameList[NameListCount++] = pwBuffer;
  672. } else {
  673. NameList[NameListCount] = NULL;
  674. CloseHandle(hFile);
  675. return(TRUE);
  676. }
  677. }
  678. } /* LoadFileToNameList() */
  679. BOOL
  680. ReadFileByLine(
  681. HANDLE hFile,
  682. PCHAR pBuffer,
  683. DWORD cbBuffer,
  684. PDWORD pcbBytesRead
  685. )
  686. {
  687. BOOL fRet;
  688. fRet = ReadFile(
  689. hFile,
  690. pBuffer,
  691. cbBuffer - 1,
  692. pcbBytesRead,
  693. NULL
  694. );
  695. if (fRet && (*pcbBytesRead > 0)) {
  696. CHAR* pNewLine;
  697. pNewLine = strstr(pBuffer, "\r\n");
  698. if (pNewLine != NULL) {
  699. LONG lOffset;
  700. lOffset = (LONG)(pNewLine + 2 - pBuffer) - (*pcbBytesRead);
  701. if (SetFilePointer(hFile, lOffset, NULL, FILE_CURRENT) ==
  702. 0xFFFFFFFF) {
  703. return(FALSE);
  704. }
  705. *pNewLine = (CHAR)NULL;
  706. }
  707. }
  708. return(fRet);
  709. }
  710. /*******************************************************************************
  711. *
  712. * Usage
  713. *
  714. * Output the usage message for this utility.
  715. *
  716. * ENTRY:
  717. * bError (input)
  718. * TRUE if the 'invalid parameter(s)' message should preceed the usage
  719. * message and the output go to stderr; FALSE for no such error
  720. * string and output goes to stdout.
  721. *
  722. * EXIT:
  723. *
  724. *
  725. ******************************************************************************/
  726. void
  727. Usage( BOOLEAN bError )
  728. {
  729. if ( bError ) {
  730. ErrorPrintf(IDS_ERROR_INVALID_PARAMETERS);
  731. ErrorPrintf(IDS_USAGE1);
  732. ErrorPrintf(IDS_USAGE2);
  733. ErrorPrintf(IDS_USAGE3);
  734. ErrorPrintf(IDS_USAGE4);
  735. ErrorPrintf(IDS_USAGE5);
  736. ErrorPrintf(IDS_USAGE6);
  737. ErrorPrintf(IDS_USAGE7);
  738. ErrorPrintf(IDS_USAGE8);
  739. ErrorPrintf(IDS_USAGE9);
  740. ErrorPrintf(IDS_USAGEA);
  741. ErrorPrintf(IDS_USAGEB);
  742. ErrorPrintf(IDS_USAGEC);
  743. ErrorPrintf(IDS_USAGED);
  744. ErrorPrintf(IDS_USAGEE);
  745. ErrorPrintf(IDS_USAGEF);
  746. }
  747. else
  748. {
  749. Message(IDS_USAGE1);
  750. Message(IDS_USAGE2);
  751. Message(IDS_USAGE3);
  752. Message(IDS_USAGE4);
  753. Message(IDS_USAGE5);
  754. Message(IDS_USAGE6);
  755. Message(IDS_USAGE7);
  756. Message(IDS_USAGE8);
  757. Message(IDS_USAGE9);
  758. Message(IDS_USAGEA);
  759. Message(IDS_USAGEB);
  760. Message(IDS_USAGEC);
  761. Message(IDS_USAGED);
  762. Message(IDS_USAGEE);
  763. Message(IDS_USAGEF);
  764. }
  765. } /* Usage() */