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.

580 lines
14 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. message.c
  5. Abstract:
  6. This module provides support routines to map DosxxxMessage APIs to
  7. the FormatMessage syntax and semantics.
  8. Author:
  9. Dan Hinsley (DanHi) 24-Sept-1991
  10. Environment:
  11. Contains NT specific code.
  12. Revision History:
  13. --*/
  14. #define ERROR_MR_MSG_TOO_LONG 316
  15. #define ERROR_MR_UN_ACC_MSGF 318
  16. #define ERROR_MR_INV_IVCOUNT 320
  17. #include <nt.h>
  18. #include <ntrtl.h>
  19. #include <nturtl.h>
  20. #include <windows.h>
  21. #include <limits.h>
  22. #include <lmcons.h>
  23. #include <lmerr.h>
  24. #include <tstring.h>
  25. #include "netascii.h"
  26. #include "netcmds.h"
  27. //
  28. // Local function declarations
  29. //
  30. BOOL
  31. FileIsConsole(
  32. HANDLE fp
  33. );
  34. VOID
  35. MyWriteConsole(
  36. HANDLE fp,
  37. LPWSTR lpBuffer,
  38. DWORD cchBuffer
  39. );
  40. //
  41. // 100 is plenty since FormatMessage only takes 99 & old DosGetMessage 9.
  42. //
  43. #define MAX_INSERT_STRINGS (100)
  44. DWORD
  45. DosGetMessageW(
  46. IN LPTSTR *InsertionStrings,
  47. IN DWORD NumberofStrings,
  48. OUT LPTSTR Buffer,
  49. IN DWORD BufferLength,
  50. IN DWORD MessageId,
  51. IN LPTSTR FileName,
  52. OUT PDWORD pMessageLength
  53. )
  54. /*++
  55. Routine Description:
  56. This maps the OS/2 DosGetMessage API to the NT FormatMessage API.
  57. Arguments:
  58. InsertionStrings - Pointer to an array of strings that will be used
  59. to replace the %n's in the message.
  60. NumberofStrings - The number of insertion strings.
  61. Buffer - The buffer to put the message into.
  62. BufferLength - The length of the supplied buffer in characters.
  63. MessageId - The message number to retrieve.
  64. FileName - The name of the message file to get the message from.
  65. pMessageLength - A pointer to return the length of the returned message.
  66. Return Value:
  67. NERR_Success
  68. ERROR_MR_MSG_TOO_LONG
  69. ERROR_MR_INV_IVCOUNT
  70. ERROR_MR_UN_ACC_MSGF
  71. ERROR_MR_MID_NOT_FOUND
  72. ERROR_INVALID_PARAMETER
  73. --*/
  74. {
  75. DWORD dwFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY;
  76. DWORD Status ;
  77. TCHAR NumberString [18];
  78. static HANDLE lpSource = NULL ;
  79. static TCHAR CurrentMsgFile[MAX_PATH] = {0,} ;
  80. //
  81. // init clear the output string
  82. //
  83. Status = NERR_Success;
  84. if (BufferLength)
  85. Buffer[0] = NULLC ;
  86. //
  87. // make sure we are not over loaded & allocate
  88. // memory for the Unicode buffer
  89. //
  90. if (NumberofStrings > MAX_INSERT_STRINGS)
  91. return ERROR_INVALID_PARAMETER ;
  92. //
  93. // See if they want to get the message from the system message file.
  94. //
  95. if (! STRCMP(FileName, OS2MSG_FILENAME)) {
  96. dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
  97. }
  98. else
  99. {
  100. //
  101. // They want it from a separate message file. Get a handle to DLL
  102. // If its for the same file as before, dont reload.
  103. //
  104. if (!(lpSource && !STRCMP(CurrentMsgFile, FileName)))
  105. {
  106. if (lpSource)
  107. {
  108. FreeLibrary(lpSource) ;
  109. }
  110. STRCPY(CurrentMsgFile, FileName) ;
  111. lpSource = LoadLibrary(FileName);
  112. if (!lpSource)
  113. {
  114. return ERROR_MR_UN_ACC_MSGF;
  115. }
  116. }
  117. dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
  118. }
  119. //
  120. // If they just want to get the message back for later formatting,
  121. // ignore the insert strings.
  122. //
  123. if (NumberofStrings == 0)
  124. {
  125. dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
  126. }
  127. //
  128. // call the Unicode version
  129. //
  130. *pMessageLength = FormatMessageW(dwFlags,
  131. lpSource,
  132. MessageId,
  133. 0, // LanguageId defaulted
  134. Buffer,
  135. BufferLength,
  136. (va_list *) InsertionStrings);
  137. //
  138. // If it failed get the return code and map it to an OS/2 equivalent
  139. //
  140. if (*pMessageLength == 0)
  141. {
  142. Buffer[0] = 0 ;
  143. Status = GetLastError();
  144. if (Status == ERROR_MR_MID_NOT_FOUND)
  145. {
  146. //
  147. // get the message number in Unicode
  148. //
  149. ultow(MessageId, NumberString, 16);
  150. //
  151. // re-setup to get it from the system. use the not found message
  152. //
  153. dwFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY |
  154. FORMAT_MESSAGE_FROM_SYSTEM;
  155. MessageId = ERROR_MR_MID_NOT_FOUND ;
  156. //
  157. // setup insert strings
  158. //
  159. InsertionStrings[0] = NumberString ;
  160. InsertionStrings[1] = FileName ;
  161. //
  162. // recall the API
  163. //
  164. *pMessageLength = FormatMessageW(dwFlags,
  165. lpSource,
  166. MessageId,
  167. 0, // LanguageId defaulted
  168. Buffer,
  169. BufferLength,
  170. (va_list *) InsertionStrings);
  171. InsertionStrings[1] = NULL ;
  172. //
  173. // revert to original error
  174. //
  175. Status = ERROR_MR_MID_NOT_FOUND ;
  176. }
  177. }
  178. //
  179. // note: NumberString don't need to be freed
  180. // since if used, they would be in the InsertionStrings which is whacked
  181. //
  182. return Status;
  183. }
  184. DWORD
  185. DosInsMessageW(
  186. IN LPTSTR *InsertionStrings,
  187. IN DWORD NumberofStrings,
  188. IN OUT LPTSTR InputMessage,
  189. IN DWORD InputMessageLength,
  190. OUT LPTSTR Buffer,
  191. IN DWORD BufferLength,
  192. OUT PDWORD pMessageLength
  193. )
  194. /*++
  195. Routine Description:
  196. This maps the OS/2 DosInsMessage API to the NT FormatMessage API.
  197. Arguments:
  198. InsertionStrings - Pointer to an array of strings that will be used
  199. to replace the %n's in the message.
  200. NumberofStrings - The number of insertion strings.
  201. InputMessage - A message with %n's to replace
  202. InputMessageLength - The length in bytes of the input message.
  203. Buffer - The buffer to put the message into.
  204. BufferLength - The length of the supplied buffer in characters.
  205. pMessageLength - A pointer to return the length of the returned message.
  206. Return Value:
  207. NERR_Success
  208. ERROR_MR_INV_IVCOUNT
  209. ERROR_MR_MSG_TOO_LONG
  210. --*/
  211. {
  212. DWORD Status ;
  213. DWORD dwFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY;
  214. UNREFERENCED_PARAMETER(InputMessageLength);
  215. //
  216. // init clear the output string
  217. //
  218. Status = NERR_Success;
  219. if (BufferLength)
  220. Buffer[0] = NULLC ;
  221. //
  222. // make sure we are not over loaded & allocate
  223. // memory for the Unicode buffer
  224. //
  225. if (NumberofStrings > MAX_INSERT_STRINGS)
  226. return ERROR_INVALID_PARAMETER ;
  227. //
  228. // This api always supplies the string to format
  229. //
  230. dwFlags |= FORMAT_MESSAGE_FROM_STRING;
  231. //
  232. // I don't know why they would call this api if they didn't have strings
  233. // to insert, but it is valid syntax.
  234. //
  235. if (NumberofStrings == 0) {
  236. dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
  237. }
  238. *pMessageLength = (WORD) FormatMessageW(dwFlags,
  239. InputMessage,
  240. 0, // ignored
  241. 0, // LanguageId defaulted
  242. Buffer,
  243. BufferLength,
  244. (va_list *)InsertionStrings);
  245. //
  246. // If it failed get the return code and map it to an OS/2 equivalent
  247. //
  248. if (*pMessageLength == 0)
  249. {
  250. Status = GetLastError();
  251. goto ExitPoint ;
  252. }
  253. ExitPoint:
  254. return Status;
  255. }
  256. VOID
  257. DosPutMessageW(
  258. HANDLE fp,
  259. LPWSTR pch,
  260. BOOL fPrintNL
  261. )
  262. {
  263. MyWriteConsole(fp,
  264. pch,
  265. wcslen(pch));
  266. //
  267. // If there's a newline at the end of the string,
  268. // print another one for formatting.
  269. //
  270. if (fPrintNL)
  271. {
  272. while (*pch && *pch != NEWLINE)
  273. {
  274. pch++;
  275. }
  276. if (*pch == NEWLINE)
  277. {
  278. MyWriteConsole(fp,
  279. L"\r\n",
  280. 2);
  281. }
  282. }
  283. }
  284. /***
  285. * PrintDependingOnLength()
  286. *
  287. * Prints out a string given to it padded to be as long as iLength. checks
  288. * the positions of the cursor in the console window. if printing the string
  289. * would go past the end of the window buffer, outputs a newline and tabs
  290. * first unless the cursor is at the start of a line.
  291. *
  292. * Args:
  293. * iLength - size of the string to be outputted. string will be padded
  294. * if necessary
  295. *
  296. * OutputString - string to output
  297. *
  298. * Returns:
  299. * returns the same value as iLength on success, -1 on failure
  300. */
  301. int
  302. PrintDependingOnLength(
  303. IN int iLength,
  304. IN LPTSTR OutputString
  305. )
  306. {
  307. CONSOLE_SCREEN_BUFFER_INFO ThisConsole;
  308. HANDLE hStdOut;
  309. //
  310. // save off iLength
  311. //
  312. int iReturn = iLength;
  313. //
  314. // Get the dimensions of the current window
  315. //
  316. hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  317. if (hStdOut != INVALID_HANDLE_VALUE)
  318. {
  319. //
  320. //init this to INT_MAX - if we aren't able to get the console screen buffer
  321. //info, then we are probably being piped to a text file (or somewhere else)
  322. //and can assume that there is no "SpaceLeft" constraint
  323. //
  324. int iSpaceLeft = INT_MAX;
  325. if (GetConsoleScreenBufferInfo(hStdOut, &ThisConsole))
  326. {
  327. //
  328. //see how much space is left in the console buffer, if we were able to
  329. //get the console info
  330. //
  331. iSpaceLeft = ThisConsole.dwSize.X - ThisConsole.dwCursorPosition.X;
  332. }
  333. //
  334. // Print out string. if we will have to handle a wrapping
  335. // column and we aren't at the beginning of a line, print a newline
  336. // and tabs first for formatting.
  337. //
  338. if ((iLength > iSpaceLeft) && (ThisConsole.dwCursorPosition.X))
  339. {
  340. WriteToCon(TEXT("\n\t\t"));
  341. }
  342. WriteToCon(TEXT("%Fws"), PaddedString(iLength, OutputString, NULL));
  343. }
  344. else
  345. {
  346. iReturn = -1;
  347. }
  348. return iReturn;
  349. }
  350. /***
  351. * FindColumnWidthAndPrintHeader()
  352. *
  353. * figures out what the correct width should be given a longest
  354. * string and a ID for a fixed header string. result will always
  355. * be whichever is longer. Once that width is figured, the function
  356. * will output the header specified by HEADER_ID
  357. *
  358. * Args:
  359. * iStringLength - integer which specifies the longest string length found
  360. * in a set of strings that is going to be outputted to the console in a column
  361. * (think the "share name" column when you do a net view <machinename>.
  362. * since the arrays of strings used by net.exe tend to vary in type, this function
  363. * assumes that you have alredy gone through and figured out which string is longest
  364. *
  365. * HEADER_ID - ID of the fixed string that will be the column header for
  366. * that set of strings. We will figure out whichever one is longest and return
  367. * that value (+ an optional TAB_DISTANCE)
  368. *
  369. * TAB_DISTANCE - distance that should the function should pad the string by
  370. * when it outputs the header (Usually 2 for it to look decent)
  371. *
  372. * Returns:
  373. * 0 or greater - success
  374. *
  375. * -1 - failure - dwHeaderID was 0, or the ID lookup failed
  376. */
  377. int
  378. FindColumnWidthAndPrintHeader(
  379. int iStringLength,
  380. const DWORD HEADER_ID,
  381. const int TAB_DISTANCE
  382. )
  383. {
  384. DWORD dwErr;
  385. WCHAR MsgBuffer[LITTLE_BUF_SIZE];
  386. DWORD dwMsgLen = sizeof(MsgBuffer) / sizeof(WCHAR);
  387. int iResultLength = -1;
  388. //
  389. // First, we need the the string specified by HEADER_ID and its length
  390. //
  391. dwErr = DosGetMessageW(IStrings,
  392. 0,
  393. MsgBuffer,
  394. LITTLE_BUF_SIZE,
  395. HEADER_ID,
  396. MESSAGE_FILENAME,
  397. &dwMsgLen);
  398. if (!dwErr)
  399. {
  400. //
  401. // Figure out which is longer - the string to
  402. // display, or the column header
  403. //
  404. iResultLength = max((int) SizeOfHalfWidthString(MsgBuffer), iStringLength);
  405. //
  406. // Add the tab length we were given
  407. //
  408. iResultLength += TAB_DISTANCE;
  409. iResultLength = PrintDependingOnLength(
  410. iResultLength,
  411. MsgBuffer
  412. );
  413. }
  414. return iResultLength;
  415. }
  416. BOOL
  417. FileIsConsole(
  418. HANDLE fp
  419. )
  420. {
  421. unsigned htype;
  422. htype = GetFileType(fp);
  423. htype &= ~FILE_TYPE_REMOTE;
  424. return htype == FILE_TYPE_CHAR;
  425. }
  426. VOID
  427. MyWriteConsole(
  428. HANDLE fp,
  429. LPWSTR lpBuffer,
  430. DWORD cchBuffer
  431. )
  432. {
  433. //
  434. // Jump through hoops for output because:
  435. //
  436. // 1. printf() family chokes on international output (stops
  437. // printing when it hits an unrecognized character)
  438. //
  439. // 2. WriteConsole() works great on international output but
  440. // fails if the handle has been redirected (i.e., when the
  441. // output is piped to a file)
  442. //
  443. // 3. WriteFile() works great when output is piped to a file
  444. // but only knows about bytes, so Unicode characters are
  445. // printed as two Ansi characters.
  446. //
  447. if (FileIsConsole(fp))
  448. {
  449. WriteConsole(fp, lpBuffer, cchBuffer, &cchBuffer, NULL);
  450. }
  451. else
  452. {
  453. LPSTR lpAnsiBuffer = (LPSTR) LocalAlloc(LMEM_FIXED, cchBuffer * sizeof(WCHAR));
  454. if (lpAnsiBuffer != NULL)
  455. {
  456. cchBuffer = WideCharToMultiByte(CP_OEMCP,
  457. 0,
  458. lpBuffer,
  459. cchBuffer,
  460. lpAnsiBuffer,
  461. cchBuffer * sizeof(WCHAR),
  462. NULL,
  463. NULL);
  464. if (cchBuffer != 0)
  465. {
  466. WriteFile(fp, lpAnsiBuffer, cchBuffer, &cchBuffer, NULL);
  467. }
  468. LocalFree(lpAnsiBuffer);
  469. }
  470. }
  471. }