4 * Copyright 1996 Alexandre Julliard
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * This code is duplicated in shlwapi. If you change something here make sure
22 * to change it in shlwapi too.
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(string
);
40 #define WPRINTF_LEFTALIGN 0x0001 /* Align output on the left ('-' prefix) */
41 #define WPRINTF_PREFIX_HEX 0x0002 /* Prefix hex with 0x ('#' prefix) */
42 #define WPRINTF_ZEROPAD 0x0004 /* Pad with zeros ('0' prefix) */
43 #define WPRINTF_LONG 0x0008 /* Long arg ('l' prefix) */
44 #define WPRINTF_SHORT 0x0010 /* Short arg ('h' prefix) */
45 #define WPRINTF_UPPER_HEX 0x0020 /* Upper-case hex ('X' specifier) */
46 #define WPRINTF_WIDE 0x0040 /* Wide arg ('w' prefix) */
47 #define WPRINTF_INTPTR 0x0080 /* Pointer-size arg ('I' prefix) */
48 #define WPRINTF_I64 0x0100 /* 64-bit arg ('I64' prefix) */
78 /***********************************************************************
79 * WPRINTF_ParseFormatA
81 * Parse a format specification. A format specification has the form:
83 * [-][#][0][width][.precision]type
85 * Return value is the length of the format specification in characters.
87 static INT
WPRINTF_ParseFormatA( LPCSTR format
, WPRINTF_FORMAT
*res
)
94 if (*p
== '-') { res
->flags
|= WPRINTF_LEFTALIGN
; p
++; }
95 if (*p
== '#') { res
->flags
|= WPRINTF_PREFIX_HEX
; p
++; }
96 if (*p
== '0') { res
->flags
|= WPRINTF_ZEROPAD
; p
++; }
97 while ((*p
>= '0') && (*p
<= '9')) /* width field */
99 res
->width
= res
->width
* 10 + *p
- '0';
102 if (*p
== '.') /* precision field */
105 while ((*p
>= '0') && (*p
<= '9'))
107 res
->precision
= res
->precision
* 10 + *p
- '0';
111 if (*p
== 'l') { res
->flags
|= WPRINTF_LONG
; p
++; }
112 else if (*p
== 'h') { res
->flags
|= WPRINTF_SHORT
; p
++; }
113 else if (*p
== 'w') { res
->flags
|= WPRINTF_WIDE
; p
++; }
116 if (p
[1] == '6' && p
[2] == '4') { res
->flags
|= WPRINTF_I64
; p
+= 3; }
117 else if (p
[1] == '3' && p
[2] == '2') p
+= 3;
118 else { res
->flags
|= WPRINTF_INTPTR
; p
++; }
123 res
->type
= (res
->flags
& WPRINTF_LONG
) ? WPR_WCHAR
: WPR_CHAR
;
126 res
->type
= (res
->flags
& WPRINTF_SHORT
) ? WPR_CHAR
: WPR_WCHAR
;
130 res
->type
= WPR_SIGNED
;
133 res
->type
= (res
->flags
& (WPRINTF_LONG
|WPRINTF_WIDE
)) ? WPR_WSTRING
: WPR_STRING
;
136 res
->type
= (res
->flags
& (WPRINTF_SHORT
|WPRINTF_WIDE
)) ? WPR_STRING
: WPR_WSTRING
;
139 res
->type
= WPR_UNSIGNED
;
142 res
->width
= 2 * sizeof(void *);
143 res
->flags
|= WPRINTF_ZEROPAD
| WPRINTF_INTPTR
;
146 res
->flags
|= WPRINTF_UPPER_HEX
;
149 res
->type
= WPR_HEXA
;
151 default: /* unknown format char */
152 res
->type
= WPR_UNKNOWN
;
153 p
--; /* print format as normal char */
156 return (INT
)(p
- format
) + 1;
160 /***********************************************************************
161 * WPRINTF_ParseFormatW
163 * Parse a format specification. A format specification has the form:
165 * [-][#][0][width][.precision]type
167 * Return value is the length of the format specification in characters.
169 static INT
WPRINTF_ParseFormatW( LPCWSTR format
, WPRINTF_FORMAT
*res
)
176 if (*p
== '-') { res
->flags
|= WPRINTF_LEFTALIGN
; p
++; }
177 if (*p
== '#') { res
->flags
|= WPRINTF_PREFIX_HEX
; p
++; }
178 if (*p
== '0') { res
->flags
|= WPRINTF_ZEROPAD
; p
++; }
179 while ((*p
>= '0') && (*p
<= '9')) /* width field */
181 res
->width
= res
->width
* 10 + *p
- '0';
184 if (*p
== '.') /* precision field */
187 while ((*p
>= '0') && (*p
<= '9'))
189 res
->precision
= res
->precision
* 10 + *p
- '0';
193 if (*p
== 'l') { res
->flags
|= WPRINTF_LONG
; p
++; }
194 else if (*p
== 'h') { res
->flags
|= WPRINTF_SHORT
; p
++; }
195 else if (*p
== 'w') { res
->flags
|= WPRINTF_WIDE
; p
++; }
198 if (p
[1] == '6' && p
[2] == '4') { res
->flags
|= WPRINTF_I64
; p
+= 3; }
199 else if (p
[1] == '3' && p
[2] == '2') p
+= 3;
200 else { res
->flags
|= WPRINTF_INTPTR
; p
++; }
205 res
->type
= (res
->flags
& WPRINTF_SHORT
) ? WPR_CHAR
: WPR_WCHAR
;
208 res
->type
= (res
->flags
& WPRINTF_LONG
) ? WPR_WCHAR
: WPR_CHAR
;
212 res
->type
= WPR_SIGNED
;
215 res
->type
= ((res
->flags
& WPRINTF_SHORT
) && !(res
->flags
& WPRINTF_WIDE
)) ? WPR_STRING
: WPR_WSTRING
;
218 res
->type
= (res
->flags
& (WPRINTF_LONG
|WPRINTF_WIDE
)) ? WPR_WSTRING
: WPR_STRING
;
221 res
->type
= WPR_UNSIGNED
;
224 res
->width
= 2 * sizeof(void *);
225 res
->flags
|= WPRINTF_ZEROPAD
| WPRINTF_INTPTR
;
228 res
->flags
|= WPRINTF_UPPER_HEX
;
231 res
->type
= WPR_HEXA
;
234 res
->type
= WPR_UNKNOWN
;
235 p
--; /* print format as normal char */
238 return (INT
)(p
- format
) + 1;
242 /***********************************************************************
245 static UINT
WPRINTF_GetLen( WPRINTF_FORMAT
*format
, WPRINTF_DATA
*arg
,
246 LPSTR number
, UINT maxlen
, BOOL dst_is_wide
)
250 if (format
->flags
& WPRINTF_LEFTALIGN
) format
->flags
&= ~WPRINTF_ZEROPAD
;
251 if (format
->width
> maxlen
) format
->width
= maxlen
;
255 return (format
->precision
= 1);
257 if (dst_is_wide
) len
= 1;
258 else len
= WideCharToMultiByte( CP_ACP
, 0, &arg
->wchar_view
, 1, NULL
, 0, NULL
, NULL
);
259 return (format
->precision
= len
);
261 if (!arg
->lpcstr_view
) arg
->lpcstr_view
= "(null)";
264 LPCSTR p
= arg
->lpcstr_view
;
265 for (len
= 0; (!format
->precision
|| len
< format
->precision
) && *p
; p
++)
267 /* This isn't applicable for UTF-8 and UTF-7 */
268 if (IsDBCSLeadByte( *p
)) p
++;
275 for (len
= 0; !format
->precision
|| (len
< format
->precision
); len
++)
276 if (!*(arg
->lpcstr_view
+ len
)) break;
278 if (len
> maxlen
) len
= maxlen
;
279 return (format
->precision
= len
);
281 if (!arg
->lpcwstr_view
) arg
->lpcwstr_view
= L
"(null)";
284 for (len
= 0; !format
->precision
|| (len
< format
->precision
); len
++)
285 if (!*(arg
->lpcwstr_view
+ len
)) break;
289 LPCWSTR p
= arg
->lpcwstr_view
;
290 for (len
= 0; (!format
->precision
|| len
< format
->precision
) && *p
; p
++)
291 len
+= WideCharToMultiByte( CP_ACP
, 0, p
, 1, NULL
, 0, NULL
, NULL
);
292 if (format
->precision
&& len
> format
->precision
) len
= format
->precision
;
294 if (len
> maxlen
) len
= maxlen
;
295 return (format
->precision
= len
);
300 const char *digits
= (format
->flags
& WPRINTF_UPPER_HEX
) ? "0123456789ABCDEF" : "0123456789abcdef";
301 ULONGLONG num
= arg
->int_view
;
302 int base
= format
->type
== WPR_HEXA
? 16 : 10;
303 char buffer
[20], *p
= buffer
, *dst
= number
;
305 if (format
->type
== WPR_SIGNED
&& arg
->int_view
< 0)
308 num
= -arg
->int_view
;
310 if (format
->flags
& WPRINTF_INTPTR
) num
= (UINT_PTR
)num
;
311 else if (!(format
->flags
& WPRINTF_I64
)) num
= (UINT
)num
;
315 *p
++ = digits
[num
% base
];
318 while (p
> buffer
) *dst
++ = *(--p
);
326 if (len
> maxlen
) len
= maxlen
;
327 if (format
->precision
< len
) format
->precision
= len
;
328 if (format
->precision
> maxlen
) format
->precision
= maxlen
;
329 if ((format
->flags
& WPRINTF_ZEROPAD
) && (format
->width
> format
->precision
))
330 format
->precision
= format
->width
;
331 if (format
->flags
& WPRINTF_PREFIX_HEX
) len
+= 2;
336 /***********************************************************************
337 * wvsnprintfA (internal)
339 static INT
wvsnprintfA( LPSTR buffer
, UINT maxlen
, LPCSTR spec
, va_list args
)
341 WPRINTF_FORMAT format
;
344 CHAR number
[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */
345 WPRINTF_DATA argData
;
347 TRACE("%p %u %s\n", buffer
, maxlen
, debugstr_a(spec
));
349 while (*spec
&& (maxlen
> 1))
351 if (*spec
!= '%') { *p
++ = *spec
++; maxlen
--; continue; }
353 if (*spec
== '%') { *p
++ = *spec
++; maxlen
--; continue; }
354 spec
+= WPRINTF_ParseFormatA( spec
, &format
);
359 argData
.wchar_view
= (WCHAR
)va_arg( args
, int );
362 argData
.char_view
= (CHAR
)va_arg( args
, int );
365 argData
.lpcstr_view
= va_arg( args
, LPCSTR
);
368 argData
.lpcwstr_view
= va_arg( args
, LPCWSTR
);
373 if (format
.flags
& WPRINTF_INTPTR
) argData
.int_view
= va_arg(args
, INT_PTR
);
374 else if (format
.flags
& WPRINTF_I64
) argData
.int_view
= va_arg(args
, LONGLONG
);
375 else argData
.int_view
= va_arg(args
, INT
);
378 argData
.wchar_view
= 0;
382 len
= WPRINTF_GetLen( &format
, &argData
, number
, maxlen
- 1, FALSE
);
384 if (!(format
.flags
& WPRINTF_LEFTALIGN
))
385 for (i
= format
.precision
; i
< format
.width
; i
++, maxlen
--)
392 if (WideCharToMultiByte( CP_ACP
, 0, &argData
.wchar_view
, 1, mb
, sizeof(mb
), NULL
, NULL
))
394 memcpy( p
, mb
, len
);
400 *p
++ = argData
.char_view
;
403 memcpy( p
, argData
.lpcstr_view
, len
);
408 LPCWSTR ptr
= argData
.lpcwstr_view
;
409 for (i
= 0; i
< len
; ptr
++)
411 CHAR mb
[5]; /* 5 is MB_LEN_MAX */
412 int ret
= WideCharToMultiByte( CP_ACP
, 0, ptr
, 1, mb
, sizeof(mb
), NULL
, NULL
);
413 if (ret
> len
- i
) ret
= len
- i
;
415 memcpy( p
, mb
, ret
);
421 if ((format
.flags
& WPRINTF_PREFIX_HEX
) && (maxlen
> 3))
424 *p
++ = (format
.flags
& WPRINTF_UPPER_HEX
) ? 'X' : 'x';
430 /* Transfer the sign now, just in case it will be zero-padded*/
431 if (number
[0] == '-')
438 for (i
= len
; i
< format
.precision
; i
++, maxlen
--) *p
++ = '0';
439 memcpy( p
, number
+ sign
, len
- sign
);
445 if (format
.flags
& WPRINTF_LEFTALIGN
)
446 for (i
= format
.precision
; i
< format
.width
; i
++, maxlen
--)
451 TRACE("%s\n",debugstr_a(buffer
));
452 return (maxlen
> 1) ? (INT
)(p
- buffer
) : -1;
456 /***********************************************************************
457 * wvsnprintfW (internal)
459 static INT
wvsnprintfW( LPWSTR buffer
, UINT maxlen
, LPCWSTR spec
, va_list args
)
461 WPRINTF_FORMAT format
;
464 CHAR number
[21]; /* 64bit number can be 18446744073709551616 which is 20 chars. and a \0 */
465 WPRINTF_DATA argData
;
467 TRACE("%p %u %s\n", buffer
, maxlen
, debugstr_w(spec
));
469 while (*spec
&& (maxlen
> 1))
471 if (*spec
!= '%') { *p
++ = *spec
++; maxlen
--; continue; }
473 if (*spec
== '%') { *p
++ = *spec
++; maxlen
--; continue; }
474 spec
+= WPRINTF_ParseFormatW( spec
, &format
);
479 argData
.wchar_view
= (WCHAR
)va_arg( args
, int );
482 argData
.char_view
= (CHAR
)va_arg( args
, int );
485 argData
.lpcstr_view
= va_arg( args
, LPCSTR
);
488 argData
.lpcwstr_view
= va_arg( args
, LPCWSTR
);
493 if (format
.flags
& WPRINTF_INTPTR
) argData
.int_view
= va_arg(args
, INT_PTR
);
494 else if (format
.flags
& WPRINTF_I64
) argData
.int_view
= va_arg(args
, LONGLONG
);
495 else argData
.int_view
= va_arg(args
, INT
);
498 argData
.wchar_view
= 0;
502 len
= WPRINTF_GetLen( &format
, &argData
, number
, maxlen
- 1, TRUE
);
504 if (!(format
.flags
& WPRINTF_LEFTALIGN
))
505 for (i
= format
.precision
; i
< format
.width
; i
++, maxlen
--)
510 *p
++ = argData
.wchar_view
;
515 if (!IsDBCSLeadByte( (BYTE
)argData
.char_view
)
516 && MultiByteToWideChar( CP_ACP
, 0, &argData
.char_view
, 1, &wc
, 1 ) > 0)
524 LPCSTR ptr
= argData
.lpcstr_view
;
525 for (i
= 0; i
< len
; i
++)
527 WCHAR buf
[2]; /* for LeadByte + NUL case, we need 2 WCHARs. */
528 int ret
, mb_len
= IsDBCSLeadByte( *ptr
) ? 2 : 1;
529 ret
= MultiByteToWideChar( CP_ACP
, 0, ptr
, mb_len
, buf
, ARRAY_SIZE( buf
));
536 if (len
) memcpy( p
, argData
.lpcwstr_view
, len
* sizeof(WCHAR
) );
540 if ((format
.flags
& WPRINTF_PREFIX_HEX
) && (maxlen
> 3))
543 *p
++ = (format
.flags
& WPRINTF_UPPER_HEX
) ? 'X' : 'x';
549 /* Transfer the sign now, just in case it will be zero-padded*/
550 if (number
[0] == '-')
557 for (i
= len
; i
< format
.precision
; i
++, maxlen
--) *p
++ = '0';
558 for (i
= sign
; i
< len
; i
++) *p
++ = (BYTE
)number
[i
];
563 if (format
.flags
& WPRINTF_LEFTALIGN
)
564 for (i
= format
.precision
; i
< format
.width
; i
++, maxlen
--)
569 TRACE("%s\n",debugstr_w(buffer
));
570 return (maxlen
> 1) ? (INT
)(p
- buffer
) : -1;
574 /***********************************************************************
575 * wvsprintfA (USER32.@)
577 INT WINAPI
wvsprintfA( LPSTR buffer
, LPCSTR spec
, va_list args
)
579 INT res
= wvsnprintfA( buffer
, 1024, spec
, args
);
580 return ( res
== -1 ) ? 1024 : res
;
584 /***********************************************************************
585 * wvsprintfW (USER32.@)
587 INT WINAPI
wvsprintfW( LPWSTR buffer
, LPCWSTR spec
, va_list args
)
589 INT res
= wvsnprintfW( buffer
, 1024, spec
, args
);
590 return ( res
== -1 ) ? 1024 : res
;
594 /***********************************************************************
595 * wsprintfA (USER32.@)
597 INT WINAPIV
wsprintfA( LPSTR buffer
, LPCSTR spec
, ... )
602 va_start( valist
, spec
);
603 res
= wvsnprintfA( buffer
, 1024, spec
, valist
);
605 return ( res
== -1 ) ? 1024 : res
;
609 /***********************************************************************
610 * wsprintfW (USER32.@)
612 INT WINAPIV
wsprintfW( LPWSTR buffer
, LPCWSTR spec
, ... )
617 va_start( valist
, spec
);
618 res
= wvsnprintfW( buffer
, 1024, spec
, valist
);
620 return ( res
== -1 ) ? 1024 : res
;