Partially fixed up tree after merge from HEAD. More to do.
[reactos.git] / reactos / dll / win32 / user32 / misc / wsprintf.c
1 /*
2 * wsprintf functions
3 *
4 * Copyright 1996 Alexandre Julliard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * NOTE:
21 * This code is duplicated in shlwapi. If you change something here make sure
22 * to change it in shlwapi too.
23 */
24 /*
25 * COPYRIGHT: See COPYING in the top level directory
26 * PROJECT: ReactOS User32
27 * PURPOSE: [w]sprintf functions
28 * FILE: lib/user32/wsprintf.c
29 * PROGRAMER: Steven Edwards
30 * REVISION HISTORY: 2003/07/13 Merged from wine user/wsprintf.c
31 * NOTES: Adapted from Wine
32 */
33
34 #include <user32.h>
35
36 #define WPRINTF_LEFTALIGN 0x0001 /* Align output on the left ('-' prefix) */
37 #define WPRINTF_PREFIX_HEX 0x0002 /* Prefix hex with 0x ('#' prefix) */
38 #define WPRINTF_ZEROPAD 0x0004 /* Pad with zeros ('0' prefix) */
39 #define WPRINTF_LONG 0x0008 /* Long arg ('l' prefix) */
40 #define WPRINTF_SHORT 0x0010 /* Short arg ('h' prefix) */
41 #define WPRINTF_UPPER_HEX 0x0020 /* Upper-case hex ('X' specifier) */
42 #define WPRINTF_WIDE 0x0040 /* Wide arg ('w' prefix) */
43
44 typedef enum
45 {
46 WPR_UNKNOWN,
47 WPR_CHAR,
48 WPR_WCHAR,
49 WPR_STRING,
50 WPR_WSTRING,
51 WPR_SIGNED,
52 WPR_UNSIGNED,
53 WPR_HEXA
54 } WPRINTF_TYPE;
55
56 typedef struct
57 {
58 UINT flags;
59 UINT width;
60 UINT precision;
61 WPRINTF_TYPE type;
62 } WPRINTF_FORMAT;
63
64 typedef union {
65 WCHAR wchar_view;
66 CHAR char_view;
67 LPCSTR lpcstr_view;
68 LPCWSTR lpcwstr_view;
69 INT int_view;
70 } WPRINTF_DATA;
71
72 static const CHAR null_stringA[] = "(null)";
73 static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };
74
75 /***********************************************************************
76 * WPRINTF_ParseFormatA
77 *
78 * Parse a format specification. A format specification has the form:
79 *
80 * [-][#][0][width][.precision]type
81 *
82 * Return value is the length of the format specification in characters.
83 */
84 static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
85 {
86 LPCSTR p = format;
87
88 res->flags = 0;
89 res->width = 0;
90 res->precision = 0;
91 if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
92 if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
93 if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
94 while ((*p >= '0') && (*p <= '9')) /* width field */
95 {
96 res->width = res->width * 10 + *p - '0';
97 p++;
98 }
99 if (*p == '.') /* precision field */
100 {
101 p++;
102 while ((*p >= '0') && (*p <= '9'))
103 {
104 res->precision = res->precision * 10 + *p - '0';
105 p++;
106 }
107 }
108 if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
109 else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
110 else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
111 switch(*p)
112 {
113 case 'c':
114 res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
115 break;
116 case 'C':
117 res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
118 break;
119 case 'd':
120 case 'i':
121 res->type = WPR_SIGNED;
122 break;
123 case 's':
124 res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
125 break;
126 case 'S':
127 res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
128 break;
129 case 'u':
130 res->type = WPR_UNSIGNED;
131 break;
132 case 'p':
133 res->width = 8;
134 res->flags |= WPRINTF_ZEROPAD;
135 /* fall through */
136 case 'X':
137 res->flags |= WPRINTF_UPPER_HEX;
138 /* fall through */
139 case 'x':
140 res->type = WPR_HEXA;
141 break;
142 default: /* unknown format char */
143 res->type = WPR_UNKNOWN;
144 p--; /* print format as normal char */
145 break;
146 }
147 return (INT)(p - format) + 1;
148 }
149
150
151 /***********************************************************************
152 * WPRINTF_ParseFormatW
153 *
154 * Parse a format specification. A format specification has the form:
155 *
156 * [-][#][0][width][.precision]type
157 *
158 * Return value is the length of the format specification in characters.
159 */
160 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
161 {
162 LPCWSTR p = format;
163
164 res->flags = 0;
165 res->width = 0;
166 res->precision = 0;
167 if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
168 if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
169 if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
170 while ((*p >= '0') && (*p <= '9')) /* width field */
171 {
172 res->width = res->width * 10 + *p - '0';
173 p++;
174 }
175 if (*p == '.') /* precision field */
176 {
177 p++;
178 while ((*p >= '0') && (*p <= '9'))
179 {
180 res->precision = res->precision * 10 + *p - '0';
181 p++;
182 }
183 }
184 if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
185 else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
186 else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
187 switch((CHAR)*p)
188 {
189 case 'c':
190 res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
191 break;
192 case 'C':
193 res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
194 break;
195 case 'd':
196 case 'i':
197 res->type = WPR_SIGNED;
198 break;
199 case 's':
200 res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
201 break;
202 case 'S':
203 res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
204 break;
205 case 'u':
206 res->type = WPR_UNSIGNED;
207 break;
208 case 'p':
209 res->width = 8;
210 res->flags |= WPRINTF_ZEROPAD;
211 /* fall through */
212 case 'X':
213 res->flags |= WPRINTF_UPPER_HEX;
214 /* fall through */
215 case 'x':
216 res->type = WPR_HEXA;
217 break;
218 default:
219 res->type = WPR_UNKNOWN;
220 p--; /* print format as normal char */
221 break;
222 }
223 return (INT)(p - format) + 1;
224 }
225
226
227 /***********************************************************************
228 * WPRINTF_GetLen
229 */
230 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
231 LPSTR number, UINT maxlen )
232 {
233 UINT len;
234
235 if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
236 if (format->width > maxlen) format->width = maxlen;
237 switch(format->type)
238 {
239 case WPR_CHAR:
240 case WPR_WCHAR:
241 return (format->precision = 1);
242 case WPR_STRING:
243 if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
244 for (len = 0; !format->precision || (len < format->precision); len++)
245 if (!*(arg->lpcstr_view + len)) break;
246 if (len > maxlen) len = maxlen;
247 return (format->precision = len);
248 case WPR_WSTRING:
249 if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
250 for (len = 0; !format->precision || (len < format->precision); len++)
251 if (!*(arg->lpcwstr_view + len)) break;
252 if (len > maxlen) len = maxlen;
253 return (format->precision = len);
254 case WPR_SIGNED:
255 len = sprintf( number, "%d", arg->int_view );
256 break;
257 case WPR_UNSIGNED:
258 len = sprintf( number, "%u", (UINT)arg->int_view );
259 break;
260 case WPR_HEXA:
261 len = sprintf( number,
262 (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
263 (UINT)arg->int_view);
264 break;
265 default:
266 return 0;
267 }
268 if (len > maxlen) len = maxlen;
269 if (format->precision < len) format->precision = len;
270 if (format->precision > maxlen) format->precision = maxlen;
271 if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
272 format->precision = format->width;
273 if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
274 return len;
275 }
276
277 /***********************************************************************
278 * wvsnprintfA (internal)
279 */
280 static INT wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
281 {
282 WPRINTF_FORMAT format;
283 LPSTR p = buffer;
284 UINT i, len, sign;
285 CHAR number[20];
286 WPRINTF_DATA argData;
287
288 //TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
289
290 while (*spec && (maxlen > 1))
291 {
292 if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
293 spec++;
294 if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
295 spec += WPRINTF_ParseFormatA( spec, &format );
296
297 switch(format.type)
298 {
299 case WPR_WCHAR:
300 argData.wchar_view = (WCHAR)va_arg( args, int );
301 break;
302 case WPR_CHAR:
303 argData.char_view = (CHAR)va_arg( args, int );
304 break;
305 case WPR_STRING:
306 argData.lpcstr_view = va_arg( args, LPCSTR );
307 break;
308 case WPR_WSTRING:
309 argData.lpcwstr_view = va_arg( args, LPCWSTR );
310 break;
311 case WPR_HEXA:
312 case WPR_SIGNED:
313 case WPR_UNSIGNED:
314 argData.int_view = va_arg( args, INT );
315 break;
316 default:
317 argData.wchar_view = 0;
318 break;
319 }
320
321 len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
322 sign = 0;
323 if (!(format.flags & WPRINTF_LEFTALIGN))
324 for (i = format.precision; i < format.width; i++, maxlen--)
325 *p++ = ' ';
326 switch(format.type)
327 {
328 case WPR_WCHAR:
329 *p++ = argData.wchar_view;
330 break;
331 case WPR_CHAR:
332 *p++ = argData.char_view;
333 break;
334 case WPR_STRING:
335 memcpy( p, argData.lpcstr_view, len );
336 p += len;
337 break;
338 case WPR_WSTRING:
339 {
340 LPCWSTR ptr = argData.lpcwstr_view;
341 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
342 }
343 break;
344 case WPR_HEXA:
345 if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
346 {
347 *p++ = '0';
348 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
349 maxlen -= 2;
350 len -= 2;
351 }
352 /* fall through */
353 case WPR_SIGNED:
354 /* Transfer the sign now, just in case it will be zero-padded*/
355 if (number[0] == '-')
356 {
357 *p++ = '-';
358 sign = 1;
359 }
360 /* fall through */
361 case WPR_UNSIGNED:
362 for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
363 memcpy( p, number + sign, len - sign );
364 p += len - sign;
365 break;
366 case WPR_UNKNOWN:
367 continue;
368 }
369 if (format.flags & WPRINTF_LEFTALIGN)
370 for (i = format.precision; i < format.width; i++, maxlen--)
371 *p++ = ' ';
372 maxlen -= len;
373 }
374 *p = 0;
375 //TRACE("%s\n",debugstr_a(buffer));
376 return (maxlen > 1) ? (INT)(p - buffer) : -1;
377 }
378
379
380 /***********************************************************************
381 * wvsnprintfW (internal)
382 */
383 static INT wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
384 {
385 WPRINTF_FORMAT format;
386 LPWSTR p = buffer;
387 UINT i, len, sign;
388 CHAR number[20];
389 WPRINTF_DATA argData;
390
391 //TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
392
393 while (*spec && (maxlen > 1))
394 {
395 if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
396 spec++;
397 if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
398 spec += WPRINTF_ParseFormatW( spec, &format );
399
400 switch(format.type)
401 {
402 case WPR_WCHAR:
403 argData.wchar_view = (WCHAR)va_arg( args, int );
404 break;
405 case WPR_CHAR:
406 argData.char_view = (CHAR)va_arg( args, int );
407 break;
408 case WPR_STRING:
409 argData.lpcstr_view = va_arg( args, LPCSTR );
410 break;
411 case WPR_WSTRING:
412 argData.lpcwstr_view = va_arg( args, LPCWSTR );
413 break;
414 case WPR_HEXA:
415 case WPR_SIGNED:
416 case WPR_UNSIGNED:
417 argData.int_view = va_arg( args, INT );
418 break;
419 default:
420 argData.wchar_view = 0;
421 break;
422 }
423
424 len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
425 sign = 0;
426 if (!(format.flags & WPRINTF_LEFTALIGN))
427 for (i = format.precision; i < format.width; i++, maxlen--)
428 *p++ = ' ';
429 switch(format.type)
430 {
431 case WPR_WCHAR:
432 *p++ = argData.wchar_view;
433 break;
434 case WPR_CHAR:
435 *p++ = argData.char_view;
436 break;
437 case WPR_STRING:
438 {
439 LPCSTR ptr = argData.lpcstr_view;
440 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
441 }
442 break;
443 case WPR_WSTRING:
444 if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
445 p += len;
446 break;
447 case WPR_HEXA:
448 if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
449 {
450 *p++ = '0';
451 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
452 maxlen -= 2;
453 len -= 2;
454 }
455 /* fall through */
456 case WPR_SIGNED:
457 /* Transfer the sign now, just in case it will be zero-padded*/
458 if (number[0] == '-')
459 {
460 *p++ = '-';
461 sign = 1;
462 }
463 /* fall through */
464 case WPR_UNSIGNED:
465 for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
466 for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
467 break;
468 case WPR_UNKNOWN:
469 continue;
470 }
471 if (format.flags & WPRINTF_LEFTALIGN)
472 for (i = format.precision; i < format.width; i++, maxlen--)
473 *p++ = ' ';
474 maxlen -= len;
475 }
476 *p = 0;
477 //TRACE("%s\n",debugstr_w(buffer));
478 return (maxlen > 1) ? (INT)(p - buffer) : -1;
479 }
480
481 /***********************************************************************
482 * wvsprintfA (USER32.@)
483 * @implemented
484 */
485 INT STDCALL wvsprintfA( LPSTR buffer, LPCSTR spec, va_list args )
486 {
487 INT res = wvsnprintfA( buffer, 1024, spec, args );
488 return ( res == -1 ) ? 1024 : res;
489 }
490
491
492 /***********************************************************************
493 * wvsprintfW (USER32.@)
494 * @implemented
495 */
496 INT STDCALL wvsprintfW( LPWSTR buffer, LPCWSTR spec, va_list args )
497 {
498 INT res = wvsnprintfW( buffer, 1024, spec, args );
499 return ( res == -1 ) ? 1024 : res;
500 }
501
502 /***********************************************************************
503 * wsprintfA (USER32.@)
504 * @implemented
505 */
506 INT CDECL wsprintfA( LPSTR buffer, LPCSTR spec, ... )
507 {
508 va_list valist;
509 INT res;
510
511 va_start( valist, spec );
512 res = wvsnprintfA( buffer, 1024, spec, valist );
513 va_end( valist );
514 return ( res == -1 ) ? 1024 : res;
515 }
516
517 /***********************************************************************
518 * wsprintfW (USER32.@)
519 * @implemented
520 */
521 INT CDECL wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
522 {
523 va_list valist;
524 INT res;
525
526 va_start( valist, spec );
527 res = wvsnprintfW( buffer, 1024, spec, valist );
528 va_end( valist );
529 return ( res == -1 ) ? 1024 : res;
530 }
531 /*
532 * @implemented
533 */
534 DWORD STDCALL WCSToMBEx(WORD CodePage,LPWSTR UnicodeString,LONG UnicodeSize,LPSTR *MBString,LONG MBSize,BOOL Allocate)
535 {
536 DWORD Size;
537 if (UnicodeSize == -1)
538 {
539 UnicodeSize = wcslen(UnicodeString)+1;
540 }
541 if (MBSize == -1)
542 {
543 if (!Allocate)
544 {
545 return 0;
546 }
547 MBSize = UnicodeSize * 2;
548 }
549 if (Allocate)
550 {
551 *MBString = RtlAllocateHeap(GetProcessHeap(), 0, MBSize);
552 }
553 if (CodePage == 0)
554 {
555 RtlUnicodeToMultiByteN(*MBString,MBSize,&Size,UnicodeString,UnicodeSize);
556 }
557 else
558 {
559 WideCharToMultiByte(CodePage,0,UnicodeString,UnicodeSize,*MBString,MBSize,0,0);
560 }
561 return UnicodeSize;
562 }
563 /*
564 * @implemented
565 */
566 DWORD STDCALL MBToWCSEx(WORD CodePage,LPSTR MBString,LONG MBSize,LPWSTR *UnicodeString,LONG UnicodeSize,BOOL Allocate)
567 {
568 DWORD Size;
569 if (MBSize == -1)
570 {
571 MBSize = strlen(MBString)+1;
572 }
573 if (UnicodeSize == -1)
574 {
575 if (!Allocate)
576 {
577 return 0;
578 }
579 UnicodeSize = MBSize;
580 }
581 if (Allocate)
582 {
583 *UnicodeString = RtlAllocateHeap(GetProcessHeap(), 0, UnicodeSize);
584 }
585 UnicodeSize *= sizeof(WCHAR);
586 if (CodePage == 0)
587 {
588 RtlMultiByteToUnicodeN(*UnicodeString,UnicodeSize,&Size,MBString,MBSize);
589 }
590 else
591 {
592 Size = MultiByteToWideChar(CodePage,0,MBString,MBSize,*UnicodeString,UnicodeSize);
593 }
594 return Size;
595 }
596 const LPWSTR strings[14] = {L"OK",L"Cancel",L"&Abort",L"&Retry",L"&Ignore",L"&Yes",L"&No",L"&Close",L"Help",L"&Try Again",L"&Continue"};
597 /*
598 * @implemented
599 */
600 LPWSTR STDCALL MB_GetString(DWORD string)
601 {
602 return heap_string_poolW(strings[string],wcslen(strings[string]));
603 }