Leaked source code of windows server 2003
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.

472 lines
16 KiB

  1. /*** message.c - Message Manager
  2. *
  3. * Microsoft Confidential
  4. * Copyright (C) Microsoft Corporation 1993-1994
  5. * All Rights Reserved.
  6. *
  7. * Author:
  8. * Benjamin W. Slivka
  9. *
  10. * History:
  11. * 10-Aug-1993 bens Initial version
  12. * 13-Aug-1993 bens Implemented message formatting
  13. * 21-Feb-1994 bens Return length of formatted string
  14. */
  15. #include <ctype.h>
  16. #include <memory.h>
  17. #include <stdarg.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include "types.h"
  22. #include "asrt.h"
  23. #include "mem.h"
  24. #include "message.h"
  25. #include "message.msg"
  26. #ifdef BIT16
  27. //** 16-bit build
  28. #ifndef HUGE
  29. #define HUGE huge
  30. #endif
  31. #ifndef FAR
  32. #define FAR far
  33. #endif
  34. #else // !BIT16
  35. //** Define away for 32-bit (NT/Chicago) build
  36. #ifndef HUGE
  37. #define HUGE
  38. #endif
  39. #ifndef FAR
  40. #define FAR
  41. #endif
  42. #endif // !BIT16
  43. typedef enum {
  44. atBAD,
  45. atSHORT,
  46. atINT,
  47. atLONG,
  48. atFLOAT,
  49. atDOUBLE,
  50. atLONGDOUBLE,
  51. atSTRING,
  52. atFARSTRING,
  53. } ARGTYPE; /* at */
  54. int addCommas(char *pszStart);
  55. ARGTYPE ATFromFormatSpecifier(char *pch);
  56. int doFinalSubstitution(char *ach, char *pszMsg, char *apszValue[]);
  57. int getHighestParmNumber(char *pszMsg);
  58. /*** MsgSet - Set a message
  59. *
  60. * NOTE: See message.h for entry/exit conditions.
  61. */
  62. int __cdecl MsgSet(char *ach, char *pszMsg, ...)
  63. {
  64. int cch;
  65. va_list marker; // For walking through function arguments
  66. char *pszFmtList; // Format string
  67. Assert(ach!=NULL);
  68. Assert(pszMsg!=NULL);
  69. va_start(marker,pszMsg); // Initialize variable arguments
  70. pszFmtList = (char *)va_arg(marker,char *); // Assume format string
  71. cch = MsgSetWorker(ach,pszMsg,pszFmtList,marker);
  72. va_end(marker); // Done with variable arguments
  73. return cch;
  74. }
  75. /*** MsgSetWorker - Set Message after va_start already called
  76. *
  77. * NOTE: See message.h for entry/exit conditions.
  78. *
  79. * Technique:
  80. * 1) Find highest parameter number in pszMsg
  81. *
  82. * If at least one parameter:
  83. * 2) Parse 3rd argument to get sprintf() format strings.
  84. * 3) Pick up each argument and format with sprintf into array
  85. *
  86. * Regardless of parameter count:
  87. * 4) Copy bytes from pszMsg to ach, replacing %N by corresponding
  88. * formatted parameter.
  89. */
  90. int MsgSetWorker(char *ach, char *pszMsg, char *pszFmtList, va_list marker)
  91. {
  92. char achFmt[32]; // Temp buffer for single format specifier
  93. char achValues[cbMSG_MAX]; // Buffer of formatted values
  94. ARGTYPE at; // Argument type
  95. char *apszValue[cMSG_PARM_MAX]; // Pointers into achValues
  96. int cch; // Length of format specifier
  97. int cParm; // Highest parameter number
  98. BOOL fCommas; // TRUE=>use Commas
  99. int iParm; // Parameter index
  100. char *pch; // Last character of format specifier
  101. char *pchFmtStart; // Start of single format specifier
  102. char *pszNextValue; // Location in achValues for next value
  103. char *pszStart;
  104. //** (1) See if we have parameters to retrieve and format
  105. cParm = getHighestParmNumber(pszMsg);
  106. if (cParm > 0) { // Need to get values
  107. //** (2) Parse 3rd argument to get sprintf() format strings.
  108. pszNextValue = achValues; // Start filling at front
  109. pch = pszFmtList; // Start at front of format specifiers
  110. for (iParm=0; iParm<cParm; iParm++) { // Retrieve and format values
  111. apszValue[iParm] = pszNextValue; // Store pointer to formatted value
  112. pchFmtStart = pch; // Remember start of specifier
  113. if (*pch != '%') { // Did not get a format specifier
  114. // Only way to report problem is in output message buffer
  115. strcpy(ach,pszMSGERR_BAD_FORMAT_SPECIFIER);
  116. AssertErrPath(pszMSGERR_BAD_FORMAT_SPECIFIER,__FILE__,__LINE__);
  117. return 0; // Failure
  118. }
  119. //** Find end of specifier
  120. pch++;
  121. while ((*pch != '\0') && (*pch != chMSG)) {
  122. pch++;
  123. }
  124. cch = (int)(pch - pchFmtStart); // Length of specifier
  125. if (cch < 2) { // Need at least % and one char for valid specifier
  126. // Only way to report problem is in output message buffer
  127. strcpy(ach,pszMSGERR_SPECIFIER_TOO_SHORT);
  128. AssertErrPath(pszMSGERR_SPECIFIER_TOO_SHORT,__FILE__,__LINE__);
  129. return 0; // Failure
  130. }
  131. //** (3) Pick up each argument and format with sprintf into array
  132. //** Get specifier for sprintf() - we need a NULL terminator
  133. fCommas = pchFmtStart[1] == ',';
  134. if (fCommas) { // Copy format, deleting comma
  135. achFmt[0] = pchFmtStart[0]; // Copy '%'
  136. memcpy(achFmt+1,pchFmtStart+2,cch-2); // Get rest after ','
  137. achFmt[cch-1] = '\0'; // Terminate specifier
  138. }
  139. else {
  140. memcpy(achFmt,pchFmtStart,cch); // Copy to specifier buffer
  141. achFmt[cch] = '\0'; // Terminate specifier
  142. }
  143. //** Format value, based on last character of format specifier
  144. at = ATFromFormatSpecifier(pch-1); // Get argument type
  145. pszStart = pszNextValue; // Save start of value (for commas)
  146. switch (at) {
  147. case atSHORT: pszNextValue += sprintf(pszNextValue,achFmt,
  148. va_arg(marker,unsigned short)) + 1;
  149. break;
  150. case atINT: pszNextValue += sprintf(pszNextValue,achFmt,
  151. va_arg(marker,unsigned int)) + 1;
  152. break;
  153. case atLONG: pszNextValue += sprintf(pszNextValue,achFmt,
  154. va_arg(marker,unsigned long)) + 1;
  155. break;
  156. case atLONGDOUBLE: pszNextValue += sprintf(pszNextValue,achFmt,
  157. #ifdef BIT16
  158. va_arg(marker,long double)) + 1;
  159. #else // !BIT16
  160. //** in 32-bit mode, long double == double
  161. va_arg(marker,double)) + 1;
  162. #endif // !BIT16
  163. break;
  164. case atDOUBLE: pszNextValue += sprintf(pszNextValue,achFmt,
  165. va_arg(marker,double)) + 1;
  166. break;
  167. case atSTRING: pszNextValue += sprintf(pszNextValue,achFmt,
  168. va_arg(marker,char *)) + 1;
  169. break;
  170. case atFARSTRING: pszNextValue += sprintf(pszNextValue,achFmt,
  171. va_arg(marker,char FAR *)) + 1;
  172. break;
  173. default:
  174. strcpy(ach,pszMSGERR_UNKNOWN_FORMAT_SPECIFIER);
  175. AssertErrPath(pszMSGERR_UNKNOWN_FORMAT_SPECIFIER,__FILE__,__LINE__);
  176. return 0; // Failure
  177. } /* switch */
  178. //**
  179. if (fCommas) {
  180. switch (at) {
  181. case atSHORT:
  182. case atINT:
  183. case atLONG:
  184. pszNextValue += addCommas(pszStart);
  185. break;
  186. }
  187. }
  188. } /* for */
  189. } /* if - parameters were present */
  190. //** (4) Copy bytes from pszMsg to ach, replacing %N parameters with values
  191. return doFinalSubstitution(ach,pszMsg,apszValue);
  192. }
  193. /*** addCommas - Add thousand separators to a number
  194. *
  195. * Entry:
  196. * pszStart - Buffer with number at end (NULL terminated)
  197. * NOTE: White space preceding or following number are
  198. * assumed to be part of the field width, and will
  199. * be consumed for use by any commas that are
  200. * added. If there are not enough blanks to account
  201. * for the commas, all the blanks will be consumed,
  202. * and the field will be effectively widened to
  203. * accomodate all of the commas.
  204. * Exit:
  205. * Returns number of commas added (0 or more)
  206. */
  207. int addCommas(char *pszStart)
  208. {
  209. char ach[20]; // Buffer for number
  210. int cb;
  211. int cbBlanksBefore;
  212. int cbBlanksAfter;
  213. int cbFirst;
  214. int cCommas;
  215. char *psz;
  216. char *pszSrc;
  217. char *pszDst;
  218. //** Figure out if there are any blanks
  219. cbBlanksBefore = strspn(pszStart," "); // Count blanks before number
  220. psz = strpbrk(pszStart+cbBlanksBefore," "); // Skip over number
  221. if (psz) {
  222. cbBlanksAfter = strspn(psz," "); // Count blanks after number
  223. cb = (int)(psz - (pszStart + cbBlanksBefore)); // Length of number itself
  224. }
  225. else {
  226. cbBlanksAfter = 0; // No blanks after number
  227. cb = strlen(pszStart+cbBlanksBefore); // Length of number itself
  228. }
  229. //** Quick out if we don't need to add commas
  230. if (cb <= 3) {
  231. return 0;
  232. }
  233. //** Figure out how many commas we need to add
  234. Assert(cb < sizeof(ach));
  235. strncpy(ach,pszStart+cbBlanksBefore,cb); // Move number to a safe place
  236. cCommas = (cb - 1) / 3; // Number of commas we need to add
  237. //** Figure out where to place modified number in buffer
  238. if ((cbBlanksBefore > 0) && (cbBlanksBefore >= cCommas)) {
  239. //** Eat some (but not all) blanks at front of buffer
  240. pszDst = pszStart + cbBlanksBefore - cCommas;
  241. }
  242. else {
  243. pszDst = pszStart; // Have to start number at front of buffer
  244. }
  245. //** Add commas to the number
  246. cbFirst = cb % 3; // Number of digits before first comma
  247. if (cbFirst == 0) {
  248. cbFirst = 3;
  249. }
  250. pszSrc = ach;
  251. strncpy(pszDst,pszSrc,cbFirst);
  252. cb -= cbFirst;
  253. pszDst += cbFirst;
  254. pszSrc += cbFirst;
  255. while (cb > 0) {
  256. *pszDst++ = chTHOUSAND_SEPARATOR; // Place comma
  257. strncpy(pszDst,pszSrc,3); // Copy next 3 digits
  258. cb -= 3;
  259. pszDst += 3;
  260. pszSrc += 3;
  261. }
  262. //** Figure out if we need to add trailing NUL
  263. if (cbBlanksBefore+cbBlanksAfter <= cCommas) {
  264. //** There were no trailing blanks to preserve, so we need to
  265. // make sure the string is terminated.
  266. *pszDst++ = '\0'; // Terminate string
  267. }
  268. //** Success
  269. return cCommas;
  270. } /* addCommas() */
  271. /*** ATFromFormatSpecifier - Determine argument type from sprintf format
  272. *
  273. * Entry:
  274. * pch - points to last character (type) of sprintf format specifier
  275. *
  276. * Exit-Success:
  277. * Returns ARGTYPE indicated by format specifier.
  278. *
  279. * Exit-Failure:
  280. * Returns atBAD -- could not determine type.
  281. */
  282. ARGTYPE ATFromFormatSpecifier(char *pch)
  283. {
  284. switch (*pch) {
  285. case 'c':
  286. case 'd':
  287. case 'i':
  288. case 'u':
  289. case 'o':
  290. case 'x':
  291. case 'X':
  292. // Check argument size character
  293. switch (*(pch-1)) {
  294. case 'h': return atSHORT;
  295. case 'l': return atLONG;
  296. default: return atINT;
  297. }
  298. break;
  299. case 'f':
  300. case 'e':
  301. case 'E':
  302. case 'g':
  303. case 'G':
  304. // Check argument size character
  305. switch (*(pch-1)) {
  306. case 'L': return atLONGDOUBLE;
  307. default: // double size
  308. // 13-Aug-1993 bens Should "%f" take a float, and "%lf" take a double?
  309. // The VC++ docs say that "%f" takes a double, but the "l" description says double,
  310. // and that omitting it cause float. I'm confused!
  311. return atDOUBLE;
  312. }
  313. break;
  314. case 's':
  315. // Check argument size character
  316. switch (*(pch-1)) {
  317. case 'F': return atFARSTRING;
  318. case 'N': return atSTRING;
  319. default: return atSTRING;
  320. }
  321. break;
  322. default:
  323. return atBAD;
  324. } /* switch */
  325. } /* ATFromFormatSpecifier */
  326. /*** doFinalSubstitution - Replace %1, %2, etc. with formatted values
  327. *
  328. * Entry:
  329. * ach - Buffer to receive final output
  330. * pszMsg - Message string, possibly with %1, %2, etc.
  331. * apszValue - Values for %1, %2, etc.
  332. *
  333. * Exit-Success:
  334. * Returns length of final text (not including NUL terminator);
  335. * ach filled in with substituted final text.
  336. *
  337. * Exit-Failure:
  338. * ach filled in with explanation of problem.
  339. */
  340. int doFinalSubstitution(char *ach, char *pszMsg, char *apszValue[])
  341. {
  342. int i;
  343. char *pch;
  344. char *pszOut;
  345. Assert(ach!=NULL);
  346. Assert(pszMsg!=NULL);
  347. pch = pszMsg; // Start scanning message at front
  348. pszOut = ach; // Fill output buffer from front
  349. while (*pch != '\0') {
  350. if (*pch == chMSG) { // Could be the start of a parameter
  351. pch++; // Skip %
  352. if (isdigit(*pch)) { // We have a parameter!
  353. i = atoi(pch); // Get number
  354. while ( (*pch != '\0') && // Skip to end of string
  355. isdigit(*pch) ) { // or end of number
  356. pch++; // Skip parameter
  357. }
  358. strcpy(pszOut,apszValue[i-1]); // Copy value
  359. pszOut += strlen(apszValue[i-1]); // Advance to end of value
  360. }
  361. else { // Not a digit
  362. *pszOut++ = chMSG; // Copy %
  363. if (*pch == chMSG) { // "%%"
  364. pch++; // Replace "%%" with single "%"
  365. }
  366. else { // Some other character
  367. *pszOut++ = *pch++; // Copy it
  368. }
  369. }
  370. }
  371. else { // Not a parameter
  372. *pszOut++ = *pch++; // Copy character
  373. }
  374. }
  375. *pszOut = '\0'; // Terminate output buffer
  376. return (int)(pszOut - ach); // Size of final string (minus NUL)
  377. }
  378. /*** getHighestParmNumber - Get number of highest %N string
  379. *
  380. * Entry:
  381. * pszMsg - String which may contain %N (%0, %1, etc.) strings
  382. *
  383. * Exit-Success:
  384. * Returns highest N found in %N string.
  385. */
  386. int getHighestParmNumber(char *pszMsg)
  387. {
  388. int i;
  389. int iMax;
  390. char *pch;
  391. Assert(pszMsg!=NULL);
  392. iMax = 0; // No parameter seen so far
  393. pch = pszMsg;
  394. while (*pch != '\0') {
  395. if (*pch == chMSG) { // Could be the start of a parameter
  396. pch++; // Skip %
  397. if (isdigit(*pch)) { // We have a parameter!
  398. i = atoi(pch); // Get number
  399. if (i > iMax) // Remember highest parameter number
  400. iMax = i;
  401. while ( (*pch != '\0') && // Skip to end of string
  402. isdigit(*pch) ) { // or end of number
  403. pch++; // Skip parameter
  404. }
  405. }
  406. else { // Not a digit
  407. pch++; // Skip it
  408. }
  409. }
  410. else { // Not a parameter
  411. pch++; // Skip it
  412. }
  413. }
  414. return iMax; // Return highest parameter seen
  415. }