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.

1181 lines
32 KiB

  1. #include "priv.h"
  2. #define DBCS_CHARSIZE (2)
  3. // N versions of wsprintf and wvsprintf which take an output buffer size to prevent overflow
  4. // bugs. Taken from the NT wsprintf source code.
  5. // _MBToWCS and _WCSToMB are actually macros which call ntrtl functions in the NT version.
  6. int _MBToWCS(LPSTR pszIn, int cchIn, LPWSTR *ppwszOut)
  7. {
  8. int cch = 0;
  9. int cbAlloc;
  10. if ((0 != cchIn) && (NULL != ppwszOut))
  11. {
  12. cchIn++;
  13. cbAlloc = cchIn * sizeof(WCHAR);
  14. *ppwszOut = (LPWSTR)LocalAlloc(LMEM_FIXED, cbAlloc);
  15. if (NULL != *ppwszOut)
  16. {
  17. cch = MultiByteToWideChar(CP_ACP, 0, pszIn, cchIn, *ppwszOut, cchIn);
  18. if (!cch)
  19. {
  20. LocalFree(*ppwszOut);
  21. *ppwszOut = NULL;
  22. }
  23. else
  24. {
  25. cch--; // Just return the number of characters
  26. }
  27. }
  28. }
  29. return cch;
  30. }
  31. int _WCSToMB(LPCWSTR pwszIn, int cchIn, LPSTR *ppszOut)
  32. {
  33. int cch = 0;
  34. int cbAlloc;
  35. if ((0 != cchIn) && (NULL != ppszOut))
  36. {
  37. cchIn++;
  38. cbAlloc = cchIn * DBCS_CHARSIZE;
  39. *ppszOut = (LPSTR)LocalAlloc(LMEM_FIXED, cbAlloc);
  40. if (NULL != *ppszOut)
  41. {
  42. cch = WideCharToMultiByte(CP_ACP, 0, pwszIn, cchIn,
  43. *ppszOut, cbAlloc, NULL, NULL);
  44. if (!cch)
  45. {
  46. LocalFree(*ppszOut);
  47. *ppszOut = NULL;
  48. }
  49. else
  50. {
  51. cch--; // Just return the number of characters
  52. }
  53. }
  54. }
  55. return cch;
  56. }
  57. /****************************** Module Header ******************************\
  58. * Module Name: wsprintf.c
  59. *
  60. * Copyright (c) 1985-91, Microsoft Corporation
  61. * sprintf.c
  62. *
  63. * Implements Windows friendly versions of sprintf and vsprintf
  64. *
  65. * History:
  66. * 2-15-89 craigc Initial
  67. * 11-12-90 MikeHar Ported from windows 3
  68. \***************************************************************************/
  69. /* Max number of characters. Doesn't include termination character */
  70. #define out(c) if (cchLimit) {*lpOut++=(c); cchLimit--;} else goto errorout
  71. /***************************************************************************\
  72. * SP_PutNumber
  73. *
  74. * Takes an unsigned long integer and places it into a buffer, respecting
  75. * a buffer limit, a radix, and a case select (upper or lower, for hex).
  76. *
  77. *
  78. * History:
  79. * 11-12-90 MikeHar Ported from windows 3 asm --> C
  80. * 12-11-90 GregoryW need to increment lpstr after assignment of mod
  81. \***************************************************************************/
  82. int SP_PutNumber(
  83. LPSTR lpstr,
  84. ULONG64 n,
  85. int limit,
  86. DWORD radix,
  87. int uppercase,
  88. int *pcch)
  89. {
  90. DWORD mod;
  91. *pcch = 0;
  92. /* It might not work for some locales or digit sets */
  93. if(uppercase)
  94. uppercase = 'A'-'0'-10;
  95. else
  96. uppercase = 'a'-'0'-10;
  97. if (limit) {
  98. do {
  99. mod = (ULONG)(n % radix);
  100. n /= radix;
  101. mod += '0';
  102. if (mod > '9')
  103. mod += uppercase;
  104. *lpstr++ = (char)mod;
  105. (*pcch)++;
  106. } while((*pcch < limit) && n);
  107. }
  108. return (n == 0) && (*pcch > 0);
  109. }
  110. /***************************************************************************\
  111. * SP_Reverse
  112. *
  113. * reverses a string in place
  114. *
  115. * History:
  116. * 11-12-90 MikeHar Ported from windows 3 asm --> C
  117. * 12-11-90 GregoryW fixed boundary conditions; removed count
  118. \***************************************************************************/
  119. void SP_Reverse(
  120. LPSTR lpFirst,
  121. LPSTR lpLast)
  122. {
  123. char ch;
  124. while(lpLast > lpFirst){
  125. ch = *lpFirst;
  126. *lpFirst++ = *lpLast;
  127. *lpLast-- = ch;
  128. }
  129. }
  130. /***************************************************************************\
  131. * SP_GetFmtValue
  132. *
  133. * reads a width or precision value from the format string
  134. *
  135. * History:
  136. * 11-12-90 MikeHar Ported from windows 3
  137. \***************************************************************************/
  138. LPCSTR SP_GetFmtValue(
  139. LPCSTR lpch,
  140. int *lpw)
  141. {
  142. int ii = 0;
  143. /* It might not work for some locales or digit sets */
  144. while (*lpch >= '0' && *lpch <= '9') {
  145. ii *= 10;
  146. ii += (int)(*lpch - '0');
  147. lpch++;
  148. }
  149. *lpw = ii;
  150. /*
  151. * return the address of the first non-digit character
  152. */
  153. return lpch;
  154. }
  155. /***************************************************************************\
  156. * SP_GetFmtValueW
  157. *
  158. * reads a width or precision value from the format string
  159. *
  160. * History:
  161. * 11-12-90 MikeHar Ported from windows 3
  162. * 07-27-92 GregoryW Created Unicode version (copied from SP_GetFmtValue)
  163. \***************************************************************************/
  164. LPCWSTR SP_GetFmtValueW(
  165. LPCWSTR lpch,
  166. int *lpw)
  167. {
  168. int ii = 0;
  169. /* It might not work for some locales or digit sets */
  170. while (*lpch >= L'0' && *lpch <= L'9') {
  171. ii *= 10;
  172. ii += (int)(*lpch - L'0');
  173. lpch++;
  174. }
  175. *lpw = ii;
  176. /*
  177. * return the address of the first non-digit character
  178. */
  179. return lpch;
  180. }
  181. /***************************************************************************\
  182. * wvsprintfA (API)
  183. *
  184. * Windows version of vsprintf(). Does not support floating point or
  185. * pointer types, and all strings are assumed to be FAR. Supports only
  186. * the left alignment flag.
  187. *
  188. * Takes pointers to an output buffer, where the string is built, a
  189. * pointer to an input buffer, and a pointer to a list of parameters.
  190. *
  191. * The cdecl function wnsprintf() calls this function.
  192. *
  193. * History:
  194. * 11-12-90 MikeHar Ported from windows 3
  195. * 12-11-90 GregoryW after %d format is parsed lpParms needs to be aligned
  196. * to a dword boundary.
  197. * 09-Aug-1991 mikeke no it doesn't
  198. * 11-19-91 DarrinM Now wvsprintf and wsprintf treat parameters the same
  199. * (as if they originated from a DWORD-aligned stack).
  200. * 1-22-97 tnoonan Converted to wvnsprintfA
  201. \***************************************************************************/
  202. LWSTDAPI_(int) wvnsprintfA(
  203. LPSTR lpOut,
  204. int cchLimitIn,
  205. LPCSTR lpFmt,
  206. va_list arglist)
  207. {
  208. BOOL fAllocateMem = FALSE;
  209. char prefix, fillch;
  210. int left, width, prec, size, sign, radix, upper, hprefix;
  211. int cchLimit = --cchLimitIn, cch, cchAvailable;
  212. LPSTR lpT, lpTMB = NULL;
  213. LPWSTR pwsz;
  214. va_list varglist = arglist;
  215. union {
  216. LONG64 l;
  217. ULONG64 ul;
  218. char sz[2];
  219. WCHAR wsz[2];
  220. } val;
  221. if (cchLimit < 0)
  222. return 0;
  223. while (*lpFmt != 0) {
  224. if (*lpFmt == '%') {
  225. /*
  226. * read the flags. These can be in any order
  227. */
  228. left = 0;
  229. prefix = 0;
  230. while (*++lpFmt) {
  231. if (*lpFmt == '-')
  232. left++;
  233. else if (*lpFmt == '#')
  234. prefix++;
  235. else
  236. break;
  237. }
  238. /*
  239. * find fill character
  240. */
  241. if (*lpFmt == '0') {
  242. fillch = '0';
  243. lpFmt++;
  244. } else
  245. fillch = ' ';
  246. /*
  247. * read the width specification
  248. */
  249. lpFmt = SP_GetFmtValue((LPCSTR)lpFmt, &cch);
  250. width = cch;
  251. /*
  252. * read the precision
  253. */
  254. if (*lpFmt == '.') {
  255. lpFmt = SP_GetFmtValue((LPCSTR)++lpFmt, &cch);
  256. prec = cch;
  257. } else
  258. prec = -1;
  259. /*
  260. * get the operand size
  261. * default size: size == 0
  262. * long number: size == 1
  263. * wide chars: size == 2
  264. * 64bit number: size == 3
  265. * It may be a good idea to check the value of size when it
  266. * is tested for non-zero below (IanJa)
  267. */
  268. hprefix = 0;
  269. if (*lpFmt == 'w') {
  270. size = 2;
  271. lpFmt++;
  272. } else if (*lpFmt == 'l') {
  273. size = 1;
  274. lpFmt++;
  275. } else if (*lpFmt == 't') {
  276. size = 0;
  277. lpFmt++;
  278. } else if (*lpFmt == 'I') {
  279. if (*(lpFmt+1) == '3' && *(lpFmt+2) == '2') {
  280. size = 1;
  281. lpFmt += 3;
  282. } else if (*(lpFmt+1) == '6' && *(lpFmt+2) == '4') {
  283. size = 3;
  284. lpFmt += 3;
  285. } else {
  286. size = (sizeof(INT_PTR) == sizeof(LONG)) ? 1 : 3;
  287. lpFmt++;
  288. }
  289. } else {
  290. size = 0;
  291. if (*lpFmt == 'h') {
  292. lpFmt++;
  293. hprefix = 1;
  294. } else if ((*lpFmt == 'i') || (*lpFmt == 'd')) {
  295. // %i or %d specified (no modifiers) - use long
  296. // %u seems to have always been short - leave alone
  297. size = 1;
  298. }
  299. }
  300. upper = 0;
  301. sign = 0;
  302. radix = 10;
  303. switch (*lpFmt) {
  304. case 0:
  305. goto errorout;
  306. case 'i':
  307. case 'd':
  308. sign++;
  309. /*** FALL THROUGH to case 'u' ***/
  310. case 'u':
  311. /* turn off prefix if decimal */
  312. prefix = 0;
  313. donumeric:
  314. /* special cases to act like MSC v5.10 */
  315. if (left || prec >= 0)
  316. fillch = ' ';
  317. /*
  318. * if size == 1, "%lu" was specified (good);
  319. * if size == 2, "%wu" was specified (bad)
  320. * if size == 3, "%p" was specified
  321. */
  322. if (size == 3) {
  323. val.l = va_arg(varglist, LONG64);
  324. } else if (size) {
  325. val.l = va_arg(varglist, long);
  326. } else if (sign) {
  327. val.l = (long)va_arg(varglist, short);
  328. } else {
  329. val.ul = va_arg(varglist, unsigned);
  330. }
  331. if (sign && val.l < 0L)
  332. val.l = -val.l;
  333. else
  334. sign = 0;
  335. /*
  336. * Unless printing a full 64-bit value, ensure values
  337. * here are not in canonical longword format to prevent
  338. * the sign extended upper 32-bits from being printed.
  339. */
  340. if (size != 3) {
  341. val.l &= MAXDWORD;
  342. }
  343. lpT = lpOut;
  344. /*
  345. * blast the number backwards into the user buffer
  346. * SP_PutNumber returns false if it runs out of space
  347. */
  348. if (!SP_PutNumber(lpOut, val.l, cchLimit, radix, upper, &cch))
  349. {
  350. break;
  351. }
  352. // Now we have the number backwards, calculate how much
  353. // more buffer space we'll need for this number to
  354. // format correctly.
  355. cchAvailable = cchLimit - cch;
  356. width -= cch;
  357. prec -= cch;
  358. if (prec > 0)
  359. {
  360. width -= prec;
  361. cchAvailable -= prec;
  362. }
  363. if (width > 0)
  364. {
  365. cchAvailable -= width - (sign ? 1 : 0);
  366. }
  367. if (sign)
  368. {
  369. cchAvailable--;
  370. }
  371. if (cchAvailable < 0)
  372. {
  373. break;
  374. }
  375. // We have enough space to format the buffer as requested
  376. // without overflowing.
  377. lpOut += cch;
  378. cchLimit -= cch;
  379. /*
  380. * fill to the field precision
  381. */
  382. while (prec-- > 0)
  383. out('0');
  384. if (width > 0 && !left) {
  385. /*
  386. * if we're filling with spaces, put sign first
  387. */
  388. if (fillch != '0') {
  389. if (sign) {
  390. sign = 0;
  391. out('-');
  392. width--;
  393. }
  394. if (prefix) {
  395. out(prefix);
  396. out('0');
  397. prefix = 0;
  398. }
  399. }
  400. if (sign)
  401. width--;
  402. /*
  403. * fill to the field width
  404. */
  405. while (width-- > 0)
  406. out(fillch);
  407. /*
  408. * still have a sign?
  409. */
  410. if (sign)
  411. out('-');
  412. if (prefix) {
  413. out(prefix);
  414. out('0');
  415. }
  416. /*
  417. * now reverse the string in place
  418. */
  419. SP_Reverse(lpT, lpOut - 1);
  420. } else {
  421. /*
  422. * add the sign character
  423. */
  424. if (sign) {
  425. out('-');
  426. width--;
  427. }
  428. if (prefix) {
  429. out(prefix);
  430. out('0');
  431. }
  432. /*
  433. * reverse the string in place
  434. */
  435. SP_Reverse(lpT, lpOut - 1);
  436. /*
  437. * pad to the right of the string in case left aligned
  438. */
  439. while (width-- > 0)
  440. out(fillch);
  441. }
  442. break;
  443. case 'p':
  444. size = (sizeof(PVOID) == sizeof(LONG)) ? 1 : 3;
  445. if (prec == -1) {
  446. prec = 2 * sizeof(PVOID);
  447. }
  448. /*** FALL THROUGH to case 'X' ***/
  449. case 'X':
  450. upper++;
  451. /*** FALL THROUGH to case 'x' ***/
  452. case 'x':
  453. radix = 16;
  454. if (prefix)
  455. if (upper)
  456. prefix = 'X';
  457. else
  458. prefix = 'x';
  459. goto donumeric;
  460. case 'C':
  461. /*
  462. * explicit size specifier overrides case
  463. */
  464. if (!size && !hprefix) {
  465. size = 1; // force WCHAR
  466. }
  467. /*** FALL THROUGH to case 'c' ***/
  468. case 'c':
  469. /*
  470. * if size == 0, "%c" or "%hc" or "%tc" was specified (CHAR)
  471. * if size == 1, "%C" or "%lc" was specified (WCHAR);
  472. * if size == 2, "%wc" was specified (WCHAR)
  473. */
  474. cch = 1; /* One character must be copied to the output buffer */
  475. if (size) {
  476. val.wsz[0] = va_arg(varglist, WCHAR);
  477. val.wsz[1] = 0x0000;
  478. pwsz = val.wsz;
  479. goto putwstring;
  480. } else {
  481. val.sz[0] = va_arg(varglist, CHAR);
  482. val.sz[1] = 0;
  483. lpT = val.sz;
  484. goto putstring;
  485. }
  486. case 'S':
  487. /*
  488. * explicit size specifier overrides case
  489. */
  490. if (!size && !hprefix) {
  491. size = 1; // force LPWSTR
  492. }
  493. /*** FALL THROUGH to case 's' ***/
  494. case 's':
  495. /*
  496. * if size == 0, "%s" or "%hs" or "%ts" was specified (LPSTR);
  497. * if size == 1, "%S" or "%ls" was specified (LPWSTR);
  498. * if size == 2, "%ws" was specified (LPWSTR)
  499. */
  500. if (size) {
  501. pwsz = va_arg(varglist, LPWSTR);
  502. if (pwsz == NULL) {
  503. cch = 0;
  504. } else {
  505. cch = wcslen(pwsz);
  506. }
  507. putwstring:
  508. cch = _WCSToMB(pwsz, cch, &lpTMB);
  509. fAllocateMem = (BOOL) cch;
  510. lpT = lpTMB;
  511. } else {
  512. lpT = va_arg(varglist, LPSTR);
  513. if (lpT == NULL) {
  514. cch = 0;
  515. } else {
  516. cch = strlen(lpT);
  517. }
  518. }
  519. putstring:
  520. if (prec >= 0 && cch > prec)
  521. cch = prec;
  522. width -= cch;
  523. if (left) {
  524. while (cch--)
  525. out(*lpT++);
  526. while (width-- > 0)
  527. out(fillch);
  528. } else {
  529. while (width-- > 0)
  530. out(fillch);
  531. while (cch--)
  532. out(*lpT++);
  533. }
  534. if (fAllocateMem) {
  535. LocalFree(lpTMB);
  536. fAllocateMem = FALSE;
  537. }
  538. break;
  539. default:
  540. normalch:
  541. if (IsDBCSLeadByte(*lpFmt)) {
  542. out(*lpFmt++);
  543. if (!*lpFmt)
  544. break; // lead byte with no trail byte
  545. }
  546. out(*lpFmt);
  547. break;
  548. } /* END OF SWITCH(*lpFmt) */
  549. } /* END OF IF(%) */ else
  550. goto normalch; /* character not a '%', just do it */
  551. /*
  552. * advance to next format string character
  553. */
  554. lpFmt++;
  555. } /* END OF OUTER WHILE LOOP */
  556. errorout:
  557. *lpOut = 0;
  558. if (fAllocateMem)
  559. {
  560. LocalFree(lpTMB);
  561. }
  562. return cchLimitIn - cchLimit;
  563. }
  564. /***************************************************************************\
  565. * StringPrintfA (API)
  566. *
  567. * Windows version of sprintf
  568. *
  569. * History:
  570. * 11-12-90 MikeHar Ported from windows 3
  571. * 02-05-90 DarrinM Cleaned up with STDARG.h vararg stuff.
  572. * 1-22-97 tnoonan Converted to wnsprintfA
  573. \***************************************************************************/
  574. LWSTDAPIV_(int) wnsprintfA(
  575. LPSTR lpOut,
  576. int cchLimitIn,
  577. LPCSTR lpFmt,
  578. ...)
  579. {
  580. va_list arglist;
  581. int ret;
  582. va_start(arglist, lpFmt);
  583. ret = wvnsprintfA(lpOut, cchLimitIn, lpFmt, arglist);
  584. va_end(arglist);
  585. return ret;
  586. }
  587. /***************************************************************************\
  588. * SP_PutNumberW
  589. *
  590. * Takes an unsigned long integer and places it into a buffer, respecting
  591. * a buffer limit, a radix, and a case select (upper or lower, for hex).
  592. *
  593. *
  594. * History:
  595. * 11-12-90 MikeHar Ported from windows 3 asm --> C
  596. * 12-11-90 GregoryW need to increment lpstr after assignment of mod
  597. * 02-11-92 GregoryW temporary version until we have C runtime support
  598. \***************************************************************************/
  599. int SP_PutNumberW(
  600. LPWSTR lpstr,
  601. ULONG64 n,
  602. int limit,
  603. DWORD radix,
  604. int uppercase,
  605. int *pcch)
  606. {
  607. DWORD mod;
  608. *pcch = 0;
  609. /* It might not work for some locales or digit sets */
  610. if(uppercase)
  611. uppercase = 'A'-'0'-10;
  612. else
  613. uppercase = 'a'-'0'-10;
  614. if (limit) {
  615. do {
  616. mod = (ULONG)(n % radix);
  617. n /= radix;
  618. mod += '0';
  619. if (mod > '9')
  620. mod += uppercase;
  621. *lpstr++ = (WCHAR)mod;
  622. (*pcch)++;
  623. } while((*pcch < limit) && n);
  624. }
  625. return (n == 0) && (*pcch > 0);
  626. }
  627. /***************************************************************************\
  628. * SP_ReverseW
  629. *
  630. * reverses a string in place
  631. *
  632. * History:
  633. * 11-12-90 MikeHar Ported from windows 3 asm --> C
  634. * 12-11-90 GregoryW fixed boundary conditions; removed count
  635. * 02-11-92 GregoryW temporary version until we have C runtime support
  636. \***************************************************************************/
  637. void SP_ReverseW(
  638. LPWSTR lpFirst,
  639. LPWSTR lpLast)
  640. {
  641. WCHAR ch;
  642. while(lpLast > lpFirst){
  643. ch = *lpFirst;
  644. *lpFirst++ = *lpLast;
  645. *lpLast-- = ch;
  646. }
  647. }
  648. /***************************************************************************\
  649. * wvsprintfW (API)
  650. *
  651. * wsprintfW() calls this function.
  652. *
  653. * History:
  654. * 11-Feb-1992 GregoryW copied xwvsprintf
  655. * Temporary hack until we have C runtime support
  656. * 1-22-97 tnoonan Converted to wvnsprintfW
  657. \***************************************************************************/
  658. LWSTDAPI_(int) wvnsprintfW(
  659. LPWSTR lpOut,
  660. int cchLimitIn,
  661. LPCWSTR lpFmt,
  662. va_list arglist)
  663. {
  664. BOOL fAllocateMem = FALSE;
  665. WCHAR prefix, fillch;
  666. int left, width, prec, size, sign, radix, upper, hprefix;
  667. int cchLimit = --cchLimitIn, cch, cchAvailable;
  668. LPWSTR lpT, lpTWC = NULL;
  669. LPBYTE psz;
  670. va_list varglist = arglist;
  671. union {
  672. LONG64 l;
  673. ULONG64 ul;
  674. char sz[2];
  675. WCHAR wsz[2];
  676. } val;
  677. if (cchLimit < 0)
  678. return 0;
  679. while (*lpFmt != 0) {
  680. if (*lpFmt == L'%') {
  681. /*
  682. * read the flags. These can be in any order
  683. */
  684. left = 0;
  685. prefix = 0;
  686. while (*++lpFmt) {
  687. if (*lpFmt == L'-')
  688. left++;
  689. else if (*lpFmt == L'#')
  690. prefix++;
  691. else
  692. break;
  693. }
  694. /*
  695. * find fill character
  696. */
  697. if (*lpFmt == L'0') {
  698. fillch = L'0';
  699. lpFmt++;
  700. } else
  701. fillch = L' ';
  702. /*
  703. * read the width specification
  704. */
  705. lpFmt = SP_GetFmtValueW(lpFmt, &cch);
  706. width = cch;
  707. /*
  708. * read the precision
  709. */
  710. if (*lpFmt == L'.') {
  711. lpFmt = SP_GetFmtValueW(++lpFmt, &cch);
  712. prec = cch;
  713. } else
  714. prec = -1;
  715. /*
  716. * get the operand size
  717. * default size: size == 0
  718. * long number: size == 1
  719. * wide chars: size == 2
  720. * 64bit number: size == 3
  721. * It may be a good idea to check the value of size when it
  722. * is tested for non-zero below (IanJa)
  723. */
  724. hprefix = 0;
  725. if ((*lpFmt == L'w') || (*lpFmt == L't')) {
  726. size = 2;
  727. lpFmt++;
  728. } else if (*lpFmt == L'l') {
  729. size = 1;
  730. lpFmt++;
  731. } else if (*lpFmt == L'I') {
  732. if (*(lpFmt+1) == L'3' && *(lpFmt+2) == L'2') {
  733. size = 1;
  734. lpFmt += 3;
  735. } else if (*(lpFmt+1) == L'6' && *(lpFmt+2) == L'4') {
  736. size = 3;
  737. lpFmt += 3;
  738. } else {
  739. size = (sizeof(INT_PTR) == sizeof(LONG)) ? 1 : 3;
  740. lpFmt++;
  741. }
  742. } else {
  743. size = 0;
  744. if (*lpFmt == L'h') {
  745. lpFmt++;
  746. hprefix = 1;
  747. } else if ((*lpFmt == L'i') || (*lpFmt == L'd')) {
  748. // %i or %d specified (no modifiers) - use long
  749. // %u seems to have always been short - leave alone
  750. size = 1;
  751. }
  752. }
  753. upper = 0;
  754. sign = 0;
  755. radix = 10;
  756. switch (*lpFmt) {
  757. case 0:
  758. goto errorout;
  759. case L'i':
  760. case L'd':
  761. sign++;
  762. /*** FALL THROUGH to case 'u' ***/
  763. case L'u':
  764. /* turn off prefix if decimal */
  765. prefix = 0;
  766. donumeric:
  767. /* special cases to act like MSC v5.10 */
  768. if (left || prec >= 0)
  769. fillch = L' ';
  770. /*
  771. * if size == 1, "%lu" was specified (good);
  772. * if size == 2, "%wu" was specified (bad)
  773. * if size == 3, "%p" was specified
  774. */
  775. if (size == 3) {
  776. val.l = va_arg(varglist, LONG64);
  777. } else if (size) {
  778. val.l = va_arg(varglist, LONG);
  779. } else if (sign) {
  780. val.l = va_arg(varglist, SHORT);
  781. } else {
  782. val.ul = va_arg(varglist, unsigned);
  783. }
  784. if (sign && val.l < 0L)
  785. val.l = -val.l;
  786. else
  787. sign = 0;
  788. /*
  789. * Unless printing a full 64-bit value, ensure values
  790. * here are not in canonical longword format to prevent
  791. * the sign extended upper 32-bits from being printed.
  792. */
  793. if (size != 3) {
  794. val.l &= MAXDWORD;
  795. }
  796. lpT = lpOut;
  797. /*
  798. * blast the number backwards into the user buffer
  799. * SP_PutNumberW returns FALSE if it runs out of space
  800. */
  801. if (!SP_PutNumberW(lpOut, val.l, cchLimit, radix, upper, &cch))
  802. {
  803. break;
  804. }
  805. // Now we have the number backwards, calculate how much
  806. // more buffer space we'll need for this number to
  807. // format correctly.
  808. cchAvailable = cchLimit - cch;
  809. width -= cch;
  810. prec -= cch;
  811. if (prec > 0)
  812. {
  813. width -= prec;
  814. cchAvailable -= prec;
  815. }
  816. if (width > 0)
  817. {
  818. cchAvailable -= width - (sign ? 1 : 0);
  819. }
  820. if (sign)
  821. {
  822. cchAvailable--;
  823. }
  824. if (cchAvailable < 0)
  825. {
  826. break;
  827. }
  828. // We have enough space to format the buffer as requested
  829. // without overflowing.
  830. lpOut += cch;
  831. cchLimit -= cch;
  832. /*
  833. * fill to the field precision
  834. */
  835. while (prec-- > 0)
  836. out(L'0');
  837. if (width > 0 && !left) {
  838. /*
  839. * if we're filling with spaces, put sign first
  840. */
  841. if (fillch != L'0') {
  842. if (sign) {
  843. sign = 0;
  844. out(L'-');
  845. width--;
  846. }
  847. if (prefix) {
  848. out(prefix);
  849. out(L'0');
  850. prefix = 0;
  851. }
  852. }
  853. if (sign)
  854. width--;
  855. /*
  856. * fill to the field width
  857. */
  858. while (width-- > 0)
  859. out(fillch);
  860. /*
  861. * still have a sign?
  862. */
  863. if (sign)
  864. out(L'-');
  865. if (prefix) {
  866. out(prefix);
  867. out(L'0');
  868. }
  869. /*
  870. * now reverse the string in place
  871. */
  872. SP_ReverseW(lpT, lpOut - 1);
  873. } else {
  874. /*
  875. * add the sign character
  876. */
  877. if (sign) {
  878. out(L'-');
  879. width--;
  880. }
  881. if (prefix) {
  882. out(prefix);
  883. out(L'0');
  884. }
  885. /*
  886. * reverse the string in place
  887. */
  888. SP_ReverseW(lpT, lpOut - 1);
  889. /*
  890. * pad to the right of the string in case left aligned
  891. */
  892. while (width-- > 0)
  893. out(fillch);
  894. }
  895. break;
  896. case L'p':
  897. size = (sizeof(PVOID) == sizeof(LONG)) ? 1 : 3;
  898. if (prec == -1) {
  899. prec = 2 * sizeof(PVOID);
  900. }
  901. /*** FALL THROUGH to case 'X' ***/
  902. case L'X':
  903. upper++;
  904. /*** FALL THROUGH to case 'x' ***/
  905. case L'x':
  906. radix = 16;
  907. if (prefix)
  908. if (upper)
  909. prefix = L'X';
  910. else
  911. prefix = L'x';
  912. goto donumeric;
  913. case L'c':
  914. if (!size && !hprefix) {
  915. size = 1; // force WCHAR
  916. }
  917. /*** FALL THROUGH to case 'C' ***/
  918. case L'C':
  919. /*
  920. * if size == 0, "%C" or "%hc" was specified (CHAR);
  921. * if size == 1, "%c" or "%lc" was specified (WCHAR);
  922. * if size == 2, "%wc" or "%tc" was specified (WCHAR)
  923. */
  924. cch = 1; /* One character must be copied to the output buffer */
  925. if (size) {
  926. val.wsz[0] = va_arg(varglist, WCHAR);
  927. val.wsz[1] = 0;
  928. lpT = val.wsz;
  929. goto putwstring;
  930. } else {
  931. val.sz[0] = va_arg(varglist, CHAR);
  932. val.sz[1] = 0;
  933. psz = val.sz;
  934. goto putstring;
  935. }
  936. case L's':
  937. if (!size && !hprefix) {
  938. size = 1; // force LPWSTR
  939. }
  940. /*** FALL THROUGH to case 'S' ***/
  941. case L'S':
  942. /*
  943. * if size == 0, "%S" or "%hs" was specified (LPSTR)
  944. * if size == 1, "%s" or "%ls" was specified (LPWSTR);
  945. * if size == 2, "%ws" or "%ts" was specified (LPWSTR)
  946. */
  947. if (size) {
  948. lpT = va_arg(varglist, LPWSTR);
  949. if (lpT == NULL) {
  950. cch = 0;
  951. } else {
  952. cch = wcslen(lpT);
  953. }
  954. } else {
  955. psz = va_arg(varglist, LPBYTE);
  956. if (psz == NULL) {
  957. cch = 0;
  958. } else {
  959. cch = strlen(psz);
  960. }
  961. putstring:
  962. cch = _MBToWCS(psz, cch, &lpTWC);
  963. fAllocateMem = (BOOL) cch;
  964. lpT = lpTWC;
  965. }
  966. putwstring:
  967. if (prec >= 0 && cch > prec)
  968. cch = prec;
  969. width -= cch;
  970. if (left) {
  971. while (cch--)
  972. out(*lpT++);
  973. while (width-- > 0)
  974. out(fillch);
  975. } else {
  976. while (width-- > 0)
  977. out(fillch);
  978. while (cch--)
  979. out(*lpT++);
  980. }
  981. if (fAllocateMem) {
  982. LocalFree(lpTWC);
  983. fAllocateMem = FALSE;
  984. }
  985. break;
  986. default:
  987. normalch:
  988. out((WCHAR)*lpFmt);
  989. break;
  990. } /* END OF SWITCH(*lpFmt) */
  991. } /* END OF IF(%) */ else
  992. goto normalch; /* character not a '%', just do it */
  993. /*
  994. * advance to next format string character
  995. */
  996. lpFmt++;
  997. } /* END OF OUTER WHILE LOOP */
  998. errorout:
  999. *lpOut = 0;
  1000. if (fAllocateMem)
  1001. {
  1002. LocalFree(lpTWC);
  1003. }
  1004. return cchLimitIn - cchLimit;
  1005. }
  1006. LWSTDAPIV_(int) wnsprintfW(
  1007. LPWSTR lpOut,
  1008. int cchLimitIn,
  1009. LPCWSTR lpFmt,
  1010. ...)
  1011. {
  1012. va_list arglist;
  1013. int ret;
  1014. va_start(arglist, lpFmt);
  1015. ret = wvnsprintfW(lpOut, cchLimitIn, lpFmt, arglist);
  1016. va_end(arglist);
  1017. return ret;
  1018. }