Added mappings for a few messages.
[wine] / dlls / user / wsprintf.c
1 /*
2  * wsprintf functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  */
6
7 #include <stdarg.h>
8 #include <string.h>
9 #include <stdio.h>
10
11 #include "winbase.h"
12 #include "wingdi.h"
13 #include "winuser.h"
14
15 #include "wine/winbase16.h"
16 #include "wine/winuser16.h"
17 #include "stackframe.h"
18
19 #include "debugtools.h"
20
21 DEFAULT_DEBUG_CHANNEL(string);
22
23
24 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
25 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
26 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
27 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
28 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
29 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
30 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
31
32 typedef enum
33 {
34     WPR_UNKNOWN,
35     WPR_CHAR,
36     WPR_WCHAR,
37     WPR_STRING,
38     WPR_WSTRING,
39     WPR_SIGNED,
40     WPR_UNSIGNED,
41     WPR_HEXA
42 } WPRINTF_TYPE;
43
44 typedef struct
45 {
46     UINT         flags;
47     UINT         width;
48     UINT         precision;
49     WPRINTF_TYPE   type;
50 } WPRINTF_FORMAT;
51
52 typedef union {
53     WCHAR   wchar_view;
54     CHAR    char_view;
55     LPCSTR  lpcstr_view;
56     LPCWSTR lpcwstr_view;
57     INT     int_view;
58 } WPRINTF_DATA;
59
60 static const CHAR null_stringA[] = "(null)";
61 static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };
62
63 /***********************************************************************
64  *           WPRINTF_ParseFormatA
65  *
66  * Parse a format specification. A format specification has the form:
67  *
68  * [-][#][0][width][.precision]type
69  *
70  * Return value is the length of the format specification in characters.
71  */
72 static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
73 {
74     LPCSTR p = format;
75
76     res->flags = 0;
77     res->width = 0;
78     res->precision = 0;
79     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
80     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
81     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
82     while ((*p >= '0') && (*p <= '9'))  /* width field */
83     {
84         res->width = res->width * 10 + *p - '0';
85         p++;
86     }
87     if (*p == '.')  /* precision field */
88     {
89         p++;
90         while ((*p >= '0') && (*p <= '9'))
91         {
92             res->precision = res->precision * 10 + *p - '0';
93             p++;
94         }
95     }
96     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
97     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
98     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
99     switch(*p)
100     {
101     case 'c':
102         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
103         break;
104     case 'C':
105         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
106         break;
107     case 'd':
108     case 'i':
109         res->type = WPR_SIGNED;
110         break;
111     case 's':
112         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
113         break;
114     case 'S':
115         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
116         break;
117     case 'u':
118         res->type = WPR_UNSIGNED;
119         break;
120     case 'X':
121         res->flags |= WPRINTF_UPPER_HEX;
122         /* fall through */
123     case 'x':
124         res->type = WPR_HEXA;
125         break;
126     default: /* unknown format char */
127         res->type = WPR_UNKNOWN;
128         p--;  /* print format as normal char */
129         break;
130     }
131     return (INT)(p - format) + 1;
132 }
133
134
135 /***********************************************************************
136  *           WPRINTF_ParseFormatW
137  *
138  * Parse a format specification. A format specification has the form:
139  *
140  * [-][#][0][width][.precision]type
141  *
142  * Return value is the length of the format specification in characters.
143  */
144 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
145 {
146     LPCWSTR p = format;
147
148     res->flags = 0;
149     res->width = 0;
150     res->precision = 0;
151     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
152     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
153     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
154     while ((*p >= '0') && (*p <= '9'))  /* width field */
155     {
156         res->width = res->width * 10 + *p - '0';
157         p++;
158     }
159     if (*p == '.')  /* precision field */
160     {
161         p++;
162         while ((*p >= '0') && (*p <= '9'))
163         {
164             res->precision = res->precision * 10 + *p - '0';
165             p++;
166         }
167     }
168     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
169     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
170     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
171     switch((CHAR)*p)
172     {
173     case 'c':
174         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
175         break;
176     case 'C':
177         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
178         break;
179     case 'd':
180     case 'i':
181         res->type = WPR_SIGNED;
182         break;
183     case 's':
184         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
185         break;
186     case 'S':
187         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
188         break;
189     case 'u':
190         res->type = WPR_UNSIGNED;
191         break;
192     case 'X':
193         res->flags |= WPRINTF_UPPER_HEX;
194         /* fall through */
195     case 'x':
196         res->type = WPR_HEXA;
197         break;
198     default:
199         res->type = WPR_UNKNOWN;
200         p--;  /* print format as normal char */
201         break;
202     }
203     return (INT)(p - format) + 1;
204 }
205
206
207 /***********************************************************************
208  *           WPRINTF_GetLen
209  */
210 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
211                               LPSTR number, UINT maxlen )
212 {
213     UINT len;
214
215     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
216     if (format->width > maxlen) format->width = maxlen;
217     switch(format->type)
218     {
219     case WPR_CHAR:
220     case WPR_WCHAR:
221         return (format->precision = 1);
222     case WPR_STRING:
223         if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
224         for (len = 0; !format->precision || (len < format->precision); len++)
225             if (!*(arg->lpcstr_view + len)) break;
226         if (len > maxlen) len = maxlen;
227         return (format->precision = len);
228     case WPR_WSTRING:
229         if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
230         for (len = 0; !format->precision || (len < format->precision); len++)
231             if (!*(arg->lpcwstr_view + len)) break;
232         if (len > maxlen) len = maxlen;
233         return (format->precision = len);
234     case WPR_SIGNED:
235         len = sprintf( number, "%d", arg->int_view );
236         break;
237     case WPR_UNSIGNED:
238         len = sprintf( number, "%u", (UINT)arg->int_view );
239         break;
240     case WPR_HEXA:
241         len = sprintf( number,
242                        (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
243                        (UINT)arg->int_view);
244         break;
245     default:
246         return 0;
247     }
248     if (len > maxlen) len = maxlen;
249     if (format->precision < len) format->precision = len;
250     if (format->precision > maxlen) format->precision = maxlen;
251     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
252         format->precision = format->width;
253     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
254     return len;
255 }
256
257
258 /***********************************************************************
259  *           wvsnprintf16   (Not a Windows API)
260  */
261 static INT16 wvsnprintf16( LPSTR buffer, UINT16 maxlen, LPCSTR spec,
262                            LPCVOID args )
263 {
264     WPRINTF_FORMAT format;
265     LPSTR p = buffer;
266     UINT i, len;
267     CHAR number[20];
268     WPRINTF_DATA cur_arg;
269     SEGPTR seg_str;
270
271     while (*spec && (maxlen > 1))
272     {
273         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
274         spec++;
275         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
276         spec += WPRINTF_ParseFormatA( spec, &format );
277         switch(format.type)
278         {
279         case WPR_WCHAR:  /* No Unicode in Win16 */
280         case WPR_CHAR:
281             cur_arg.char_view = VA_ARG16( args, CHAR );
282             break;
283         case WPR_WSTRING:  /* No Unicode in Win16 */
284         case WPR_STRING:
285             seg_str = VA_ARG16( args, SEGPTR );
286             if (IsBadReadPtr16(seg_str, 1 )) cur_arg.lpcstr_view = "";
287             else cur_arg.lpcstr_view = MapSL( seg_str );
288             break;
289         case WPR_SIGNED:
290             if (!(format.flags & WPRINTF_LONG))
291             {
292                 cur_arg.int_view = VA_ARG16( args, INT16 );
293                 break;
294             }
295             /* fall through */
296         case WPR_HEXA:
297         case WPR_UNSIGNED:
298             if (format.flags & WPRINTF_LONG)
299                 cur_arg.int_view = VA_ARG16( args, UINT );
300             else
301                 cur_arg.int_view = VA_ARG16( args, UINT16 );
302             break;
303         case WPR_UNKNOWN:
304             continue;
305         }
306         len = WPRINTF_GetLen( &format, &cur_arg, number, maxlen - 1 );
307         if (!(format.flags & WPRINTF_LEFTALIGN))
308             for (i = format.precision; i < format.width; i++, maxlen--)
309                 *p++ = ' ';
310         switch(format.type)
311         {
312         case WPR_WCHAR:  /* No Unicode in Win16 */
313         case WPR_CHAR:
314             *p= cur_arg.char_view;
315             if (*p != '\0') p++;
316             else if (format.width > 1) *p++ = ' ';
317             else len = 0;
318             break;
319         case WPR_WSTRING:  /* No Unicode in Win16 */
320         case WPR_STRING:
321             if (len) memcpy( p, cur_arg.lpcstr_view, len );
322             p += len;
323             break;
324         case WPR_HEXA:
325             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
326             {
327                 *p++ = '0';
328                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
329                 maxlen -= 2;
330                 len -= 2;
331             }
332             /* fall through */
333         case WPR_SIGNED:
334         case WPR_UNSIGNED:
335             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
336             if (len) memcpy( p, number, len );
337             p += len;
338             break;
339         case WPR_UNKNOWN:
340             continue;
341         }
342         if (format.flags & WPRINTF_LEFTALIGN)
343             for (i = format.precision; i < format.width; i++, maxlen--)
344                 *p++ = ' ';
345         maxlen -= len;
346     }
347     *p = 0;
348     return (maxlen > 1) ? (INT)(p - buffer) : -1;
349 }
350
351
352 /***********************************************************************
353  *           wvsnprintfA   (USER32.@) (Not a Windows API, but we export it from USER32 anyway)
354  */
355 INT WINAPI wvsnprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args )
356 {
357     WPRINTF_FORMAT format;
358     LPSTR p = buffer;
359     UINT i, len;
360     CHAR number[20];
361     WPRINTF_DATA argData;
362
363     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
364
365     while (*spec && (maxlen > 1))
366     {
367         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
368         spec++;
369         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
370         spec += WPRINTF_ParseFormatA( spec, &format );
371
372         switch(format.type)
373         {
374         case WPR_WCHAR:
375             argData.wchar_view = (WCHAR)va_arg( args, int );
376             break;
377         case WPR_CHAR:
378             argData.char_view = (CHAR)va_arg( args, int );
379             break;
380         case WPR_STRING:
381             argData.lpcstr_view = va_arg( args, LPCSTR );
382             break;
383         case WPR_WSTRING:
384             argData.lpcwstr_view = va_arg( args, LPCWSTR );
385             break;
386         case WPR_HEXA:
387         case WPR_SIGNED:
388         case WPR_UNSIGNED:
389             argData.int_view = va_arg( args, INT );
390             break;
391         default:
392             argData.wchar_view = 0;
393             break;
394         }
395
396         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
397         if (!(format.flags & WPRINTF_LEFTALIGN))
398             for (i = format.precision; i < format.width; i++, maxlen--)
399                 *p++ = ' ';
400         switch(format.type)
401         {
402         case WPR_WCHAR:
403             *p = argData.wchar_view;
404             if (*p != '\0') p++;
405             else if (format.width > 1) *p++ = ' ';
406             else len = 0;
407             break;
408         case WPR_CHAR:
409             *p = argData.char_view;
410             if (*p != '\0') p++;
411             else if (format.width > 1) *p++ = ' ';
412             else len = 0;
413             break;
414         case WPR_STRING:
415             memcpy( p, argData.lpcstr_view, len );
416             p += len;
417             break;
418         case WPR_WSTRING:
419             {
420                 LPCWSTR ptr = argData.lpcwstr_view;
421                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
422             }
423             break;
424         case WPR_HEXA:
425             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
426             {
427                 *p++ = '0';
428                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
429                 maxlen -= 2;
430                 len -= 2;
431             }
432             /* fall through */
433         case WPR_SIGNED:
434         case WPR_UNSIGNED:
435             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
436             memcpy( p, number, len );
437             p += len;
438             break;
439         case WPR_UNKNOWN:
440             continue;
441         }
442         if (format.flags & WPRINTF_LEFTALIGN)
443             for (i = format.precision; i < format.width; i++, maxlen--)
444                 *p++ = ' ';
445         maxlen -= len;
446     }
447     *p = 0;
448     TRACE("%s\n",debugstr_a(buffer));
449     return (maxlen > 1) ? (INT)(p - buffer) : -1;
450 }
451
452
453 /***********************************************************************
454  *           wvsnprintfW   (USER32.@) (Not a Windows API, but we export it from USER32 anyway)
455  */
456 INT WINAPI wvsnprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args )
457 {
458     WPRINTF_FORMAT format;
459     LPWSTR p = buffer;
460     UINT i, len;
461     CHAR number[20];
462     WPRINTF_DATA argData;
463
464     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
465
466     while (*spec && (maxlen > 1))
467     {
468         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
469         spec++;
470         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
471         spec += WPRINTF_ParseFormatW( spec, &format );
472
473         switch(format.type)
474         {
475         case WPR_WCHAR:
476             argData.wchar_view = (WCHAR)va_arg( args, int );
477             break;
478         case WPR_CHAR:
479             argData.char_view = (CHAR)va_arg( args, int );
480             break;
481         case WPR_STRING:
482             argData.lpcstr_view = va_arg( args, LPCSTR );
483             break;
484         case WPR_WSTRING:
485             argData.lpcwstr_view = va_arg( args, LPCWSTR );
486             break;
487         case WPR_HEXA:
488         case WPR_SIGNED:
489         case WPR_UNSIGNED:
490             argData.int_view = va_arg( args, INT );
491             break;
492         default:
493             argData.wchar_view = 0;
494             break;
495         }
496
497         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
498         if (!(format.flags & WPRINTF_LEFTALIGN))
499             for (i = format.precision; i < format.width; i++, maxlen--)
500                 *p++ = ' ';
501         switch(format.type)
502         {
503         case WPR_WCHAR:
504             *p = argData.wchar_view;
505             if (*p != '\0') p++;
506             else if (format.width > 1) *p++ = ' ';
507             else len = 0;
508             break;
509         case WPR_CHAR:
510             *p = argData.char_view;
511             if (*p != '\0') p++;
512             else if (format.width > 1) *p++ = ' ';
513             else len = 0;
514             break;
515         case WPR_STRING:
516             {
517                 LPCSTR ptr = argData.lpcstr_view;
518                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
519             }
520             break;
521         case WPR_WSTRING:
522             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
523             p += len;
524             break;
525         case WPR_HEXA:
526             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
527             {
528                 *p++ = '0';
529                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
530                 maxlen -= 2;
531                 len -= 2;
532             }
533             /* fall through */
534         case WPR_SIGNED:
535         case WPR_UNSIGNED:
536             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
537             for (i = 0; i < len; i++) *p++ = (WCHAR)number[i];
538             break;
539         case WPR_UNKNOWN:
540             continue;
541         }
542         if (format.flags & WPRINTF_LEFTALIGN)
543             for (i = format.precision; i < format.width; i++, maxlen--)
544                 *p++ = ' ';
545         maxlen -= len;
546     }
547     *p = 0;
548     TRACE("%s\n",debugstr_w(buffer));
549     return (maxlen > 1) ? (INT)(p - buffer) : -1;
550 }
551
552
553 /***********************************************************************
554  *           wvsprintf   (USER.421)
555  */
556 INT16 WINAPI wvsprintf16( LPSTR buffer, LPCSTR spec, LPCVOID args )
557 {
558     INT16 res;
559
560     TRACE("for %p got:\n",buffer);
561     res = wvsnprintf16( buffer, 1024, spec, args );
562     return ( res == -1 ) ? 1024 : res;
563 }
564
565
566 /***********************************************************************
567  *           wvsprintfA   (USER32.@)
568  */
569 INT WINAPI wvsprintfA( LPSTR buffer, LPCSTR spec, va_list args )
570 {
571     INT res = wvsnprintfA( buffer, 1024, spec, args );
572     return ( res == -1 ) ? 1024 : res;
573 }
574
575
576 /***********************************************************************
577  *           wvsprintfW   (USER32.@)
578  */
579 INT WINAPI wvsprintfW( LPWSTR buffer, LPCWSTR spec, va_list args )
580 {
581     INT res = wvsnprintfW( buffer, 1024, spec, args );
582     return ( res == -1 ) ? 1024 : res;
583 }
584
585
586 /***********************************************************************
587  *           _wsprintf   (USER.420)
588  */
589 INT16 WINAPIV wsprintf16(void)
590 {
591     VA_LIST16 valist;
592     INT16 res;
593     SEGPTR buffer, spec;
594
595     VA_START16( valist );
596     buffer = VA_ARG16( valist, SEGPTR );
597     spec   = VA_ARG16( valist, SEGPTR );
598     res = wvsnprintf16( MapSL(buffer), 1024, MapSL(spec), valist );
599     VA_END16( valist );
600     return ( res == -1 ) ? 1024 : res;
601 }
602
603
604 /***********************************************************************
605  *           wsprintfA   (USER32.@)
606  */
607 INT WINAPIV wsprintfA( LPSTR buffer, LPCSTR spec, ... )
608 {
609     va_list valist;
610     INT res;
611
612     va_start( valist, spec );
613     res = wvsnprintfA( buffer, 1024, spec, valist );
614     va_end( valist );
615     return ( res == -1 ) ? 1024 : res;
616 }
617
618
619 /***********************************************************************
620  *           wsprintfW   (USER32.@)
621  */
622 INT WINAPIV wsprintfW( LPWSTR buffer, LPCWSTR spec, ... )
623 {
624     va_list valist;
625     INT res;
626
627     va_start( valist, spec );
628     res = wvsnprintfW( buffer, 1024, spec, valist );
629     va_end( valist );
630     return ( res == -1 ) ? 1024 : res;
631 }