hlink: Implement HlinkClone.
[wine] / dlls / shlwapi / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTE:
21  * This code is duplicated in user32. If you change something here make sure
22  * to change it in user32 too.
23  */
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include <stdio.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #define NO_SHLWAPI_REG
32 #include "shlwapi.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(string);
37
38
39 #define WPRINTF_LEFTALIGN   0x0001  /* Align output on the left ('-' prefix) */
40 #define WPRINTF_PREFIX_HEX  0x0002  /* Prefix hex with 0x ('#' prefix) */
41 #define WPRINTF_ZEROPAD     0x0004  /* Pad with zeros ('0' prefix) */
42 #define WPRINTF_LONG        0x0008  /* Long arg ('l' prefix) */
43 #define WPRINTF_SHORT       0x0010  /* Short arg ('h' prefix) */
44 #define WPRINTF_UPPER_HEX   0x0020  /* Upper-case hex ('X' specifier) */
45 #define WPRINTF_WIDE        0x0040  /* Wide arg ('w' prefix) */
46
47 typedef enum
48 {
49     WPR_UNKNOWN,
50     WPR_CHAR,
51     WPR_WCHAR,
52     WPR_STRING,
53     WPR_WSTRING,
54     WPR_SIGNED,
55     WPR_UNSIGNED,
56     WPR_HEXA
57 } WPRINTF_TYPE;
58
59 typedef struct
60 {
61     UINT         flags;
62     UINT         width;
63     UINT         precision;
64     WPRINTF_TYPE   type;
65 } WPRINTF_FORMAT;
66
67 typedef union {
68     WCHAR   wchar_view;
69     CHAR    char_view;
70     LPCSTR  lpcstr_view;
71     LPCWSTR lpcwstr_view;
72     INT     int_view;
73 } WPRINTF_DATA;
74
75 static const CHAR null_stringA[] = "(null)";
76 static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 };
77
78 /***********************************************************************
79  *           WPRINTF_ParseFormatA
80  *
81  * Parse a format specification. A format specification has the form:
82  *
83  * [-][#][0][width][.precision]type
84  *
85  * Return value is the length of the format specification in characters.
86  */
87 static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res )
88 {
89     LPCSTR p = format;
90
91     res->flags = 0;
92     res->width = 0;
93     res->precision = 0;
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 */
98     {
99         res->width = res->width * 10 + *p - '0';
100         p++;
101     }
102     if (*p == '.')  /* precision field */
103     {
104         p++;
105         while ((*p >= '0') && (*p <= '9'))
106         {
107             res->precision = res->precision * 10 + *p - '0';
108             p++;
109         }
110     }
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++; }
114     switch(*p)
115     {
116     case 'c':
117         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
118         break;
119     case 'C':
120         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
121         break;
122     case 'd':
123     case 'i':
124         res->type = WPR_SIGNED;
125         break;
126     case 's':
127         res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
128         break;
129     case 'S':
130         res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
131         break;
132     case 'u':
133         res->type = WPR_UNSIGNED;
134         break;
135     case 'X':
136         res->flags |= WPRINTF_UPPER_HEX;
137         /* fall through */
138     case 'x':
139         res->type = WPR_HEXA;
140         break;
141     default: /* unknown format char */
142         res->type = WPR_UNKNOWN;
143         p--;  /* print format as normal char */
144         break;
145     }
146     return (INT)(p - format) + 1;
147 }
148
149
150 /***********************************************************************
151  *           WPRINTF_ParseFormatW
152  *
153  * Parse a format specification. A format specification has the form:
154  *
155  * [-][#][0][width][.precision]type
156  *
157  * Return value is the length of the format specification in characters.
158  */
159 static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res )
160 {
161     LPCWSTR p = format;
162
163     res->flags = 0;
164     res->width = 0;
165     res->precision = 0;
166     if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; }
167     if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; }
168     if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; }
169     while ((*p >= '0') && (*p <= '9'))  /* width field */
170     {
171         res->width = res->width * 10 + *p - '0';
172         p++;
173     }
174     if (*p == '.')  /* precision field */
175     {
176         p++;
177         while ((*p >= '0') && (*p <= '9'))
178         {
179             res->precision = res->precision * 10 + *p - '0';
180             p++;
181         }
182     }
183     if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; }
184     else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; }
185     else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; }
186     switch((CHAR)*p)
187     {
188     case 'c':
189         res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR;
190         break;
191     case 'C':
192         res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR;
193         break;
194     case 'd':
195     case 'i':
196         res->type = WPR_SIGNED;
197         break;
198     case 's':
199         res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING;
200         break;
201     case 'S':
202         res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING;
203         break;
204     case 'u':
205         res->type = WPR_UNSIGNED;
206         break;
207     case 'X':
208         res->flags |= WPRINTF_UPPER_HEX;
209         /* fall through */
210     case 'x':
211         res->type = WPR_HEXA;
212         break;
213     default:
214         res->type = WPR_UNKNOWN;
215         p--;  /* print format as normal char */
216         break;
217     }
218     return (INT)(p - format) + 1;
219 }
220
221
222 /***********************************************************************
223  *           WPRINTF_GetLen
224  */
225 static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg,
226                               LPSTR number, UINT maxlen )
227 {
228     UINT len;
229
230     if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD;
231     if (format->width > maxlen) format->width = maxlen;
232     switch(format->type)
233     {
234     case WPR_CHAR:
235     case WPR_WCHAR:
236         return (format->precision = 1);
237     case WPR_STRING:
238         if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA;
239         for (len = 0; !format->precision || (len < format->precision); len++)
240             if (!*(arg->lpcstr_view + len)) break;
241         if (len > maxlen) len = maxlen;
242         return (format->precision = len);
243     case WPR_WSTRING:
244         if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW;
245         for (len = 0; !format->precision || (len < format->precision); len++)
246             if (!*(arg->lpcwstr_view + len)) break;
247         if (len > maxlen) len = maxlen;
248         return (format->precision = len);
249     case WPR_SIGNED:
250         len = sprintf( number, "%d", arg->int_view );
251         break;
252     case WPR_UNSIGNED:
253         len = sprintf( number, "%u", (UINT)arg->int_view );
254         break;
255     case WPR_HEXA:
256         len = sprintf( number,
257                        (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x",
258                        (UINT)arg->int_view);
259         break;
260     default:
261         return 0;
262     }
263     if (len > maxlen) len = maxlen;
264     if (format->precision < len) format->precision = len;
265     if (format->precision > maxlen) format->precision = maxlen;
266     if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision))
267         format->precision = format->width;
268     if (format->flags & WPRINTF_PREFIX_HEX) len += 2;
269     return len;
270 }
271
272
273 /***********************************************************************
274  *           wvnsprintfA   (SHLWAPI.@)
275  *
276  * Print formatted output to a string, up to a maximum number of chars.
277  *
278  * PARAMS
279  * buffer [O] Destination for output string
280  * maxlen [I] Maximum number of characters to write
281  * spec   [I] Format string
282  *
283  * RETURNS
284  *  Success: The number of characters written.
285  *  Failure: -1.
286  */
287 INT WINAPI wvnsprintfA( LPSTR buffer, INT maxlen, LPCSTR spec, __ms_va_list args )
288 {
289     WPRINTF_FORMAT format;
290     LPSTR p = buffer;
291     UINT i, len, sign;
292     CHAR number[20];
293     WPRINTF_DATA argData;
294
295     TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec));
296
297     while (*spec && (maxlen > 1))
298     {
299         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
300         spec++;
301         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
302         spec += WPRINTF_ParseFormatA( spec, &format );
303
304         switch(format.type)
305         {
306         case WPR_WCHAR:
307             argData.wchar_view = (WCHAR)va_arg( args, int );
308             break;
309         case WPR_CHAR:
310             argData.char_view = (CHAR)va_arg( args, int );
311             break;
312         case WPR_STRING:
313             argData.lpcstr_view = va_arg( args, LPCSTR );
314             break;
315         case WPR_WSTRING:
316             argData.lpcwstr_view = va_arg( args, LPCWSTR );
317             break;
318         case WPR_HEXA:
319         case WPR_SIGNED:
320         case WPR_UNSIGNED:
321             argData.int_view = va_arg( args, INT );
322             break;
323         default:
324             argData.wchar_view = 0;
325             break;
326         }
327
328         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
329         sign = 0;
330         if (!(format.flags & WPRINTF_LEFTALIGN))
331             for (i = format.precision; i < format.width; i++, maxlen--)
332                 *p++ = ' ';
333         switch(format.type)
334         {
335         case WPR_WCHAR:
336             *p++ = argData.wchar_view;
337             break;
338         case WPR_CHAR:
339             *p++ = argData.char_view;
340             break;
341         case WPR_STRING:
342             memcpy( p, argData.lpcstr_view, len );
343             p += len;
344             break;
345         case WPR_WSTRING:
346             {
347                 LPCWSTR ptr = argData.lpcwstr_view;
348                 for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++;
349             }
350             break;
351         case WPR_HEXA:
352             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
353             {
354                 *p++ = '0';
355                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
356                 maxlen -= 2;
357                 len -= 2;
358             }
359             /* fall through */
360         case WPR_SIGNED:
361             /* Transfer the sign now, just in case it will be zero-padded*/
362             if (number[0] == '-')
363             {
364                 *p++ = '-';
365                 sign = 1;
366             }
367             /* fall through */
368         case WPR_UNSIGNED:
369             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
370             memcpy( p, number + sign, len - sign  );
371             p += len - sign;
372             break;
373         case WPR_UNKNOWN:
374             continue;
375         }
376         if (format.flags & WPRINTF_LEFTALIGN)
377             for (i = format.precision; i < format.width; i++, maxlen--)
378                 *p++ = ' ';
379         maxlen -= len;
380     }
381     *p = 0;
382     TRACE("%s\n",debugstr_a(buffer));
383     return (maxlen > 1) ? (INT)(p - buffer) : -1;
384 }
385
386
387 /***********************************************************************
388  *           wvnsprintfW   (SHLWAPI.@)
389  *
390  * See wvnsprintfA.
391  */
392 INT WINAPI wvnsprintfW( LPWSTR buffer, INT maxlen, LPCWSTR spec, __ms_va_list args )
393 {
394     WPRINTF_FORMAT format;
395     LPWSTR p = buffer;
396     UINT i, len, sign;
397     CHAR number[20];
398     WPRINTF_DATA argData;
399
400     TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec));
401
402     while (*spec && (maxlen > 1))
403     {
404         if (*spec != '%') { *p++ = *spec++; maxlen--; continue; }
405         spec++;
406         if (*spec == '%') { *p++ = *spec++; maxlen--; continue; }
407         spec += WPRINTF_ParseFormatW( spec, &format );
408
409         switch(format.type)
410         {
411         case WPR_WCHAR:
412             argData.wchar_view = (WCHAR)va_arg( args, int );
413             break;
414         case WPR_CHAR:
415             argData.char_view = (CHAR)va_arg( args, int );
416             break;
417         case WPR_STRING:
418             argData.lpcstr_view = va_arg( args, LPCSTR );
419             break;
420         case WPR_WSTRING:
421             argData.lpcwstr_view = va_arg( args, LPCWSTR );
422             break;
423         case WPR_HEXA:
424         case WPR_SIGNED:
425         case WPR_UNSIGNED:
426             argData.int_view = va_arg( args, INT );
427             break;
428         default:
429             argData.wchar_view = 0;
430             break;
431         }
432
433         len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 );
434         sign = 0;
435         if (!(format.flags & WPRINTF_LEFTALIGN))
436             for (i = format.precision; i < format.width; i++, maxlen--)
437                 *p++ = ' ';
438         switch(format.type)
439         {
440         case WPR_WCHAR:
441             *p++ = argData.wchar_view;
442             break;
443         case WPR_CHAR:
444             *p++ = argData.char_view;
445             break;
446         case WPR_STRING:
447             {
448                 LPCSTR ptr = argData.lpcstr_view;
449                 for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++;
450             }
451             break;
452         case WPR_WSTRING:
453             if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) );
454             p += len;
455             break;
456         case WPR_HEXA:
457             if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3))
458             {
459                 *p++ = '0';
460                 *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x';
461                 maxlen -= 2;
462                 len -= 2;
463             }
464             /* fall through */
465         case WPR_SIGNED:
466             /* Transfer the sign now, just in case it will be zero-padded*/
467             if (number[0] == '-')
468             {
469                 *p++ = '-';
470                 sign = 1;
471             }
472             /* fall through */
473         case WPR_UNSIGNED:
474             for (i = len; i < format.precision; i++, maxlen--) *p++ = '0';
475             for (i = sign; i < len; i++) *p++ = (WCHAR)number[i];
476             break;
477         case WPR_UNKNOWN:
478             continue;
479         }
480         if (format.flags & WPRINTF_LEFTALIGN)
481             for (i = format.precision; i < format.width; i++, maxlen--)
482                 *p++ = ' ';
483         maxlen -= len;
484     }
485     *p = 0;
486     TRACE("%s\n",debugstr_w(buffer));
487     return (maxlen > 1) ? (INT)(p - buffer) : -1;
488 }
489
490
491 /*************************************************************************
492  *           wnsprintfA   (SHLWAPI.@)
493  *
494  * Print formatted output to a string, up to a maximum number of chars.
495  *
496  * PARAMS
497  * lpOut      [O] Destination for output string
498  * cchLimitIn [I] Maximum number of characters to write
499  * lpFmt      [I] Format string
500  *
501  * RETURNS
502  *  Success: The number of characters written.
503  *  Failure: -1.
504  */
505 int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...)
506 {
507     __ms_va_list valist;
508     INT res;
509
510     __ms_va_start( valist, lpFmt );
511     res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist );
512     __ms_va_end( valist );
513     return res;
514 }
515
516
517 /*************************************************************************
518  *           wnsprintfW   (SHLWAPI.@)
519  *
520  * See wnsprintfA.
521  */
522 int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...)
523 {
524     __ms_va_list valist;
525     INT res;
526
527     __ms_va_start( valist, lpFmt );
528     res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist );
529     __ms_va_end( valist );
530     return res;
531 }