mmdevapi/tests: Fix wrong buffer unit and memory leaks.
[wine] / dlls / msvcrt / printf.h
1 /*
2  * Copyright 2011 Piotr Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #ifdef PRINTF_WIDE
20 #define APICHAR MSVCRT_wchar_t
21 #define CONVCHAR char
22 #define FUNC_NAME(func) func ## _w
23 #else
24 #define APICHAR char
25 #define CONVCHAR MSVCRT_wchar_t
26 #define FUNC_NAME(func) func ## _a
27 #endif
28
29 typedef struct FUNC_NAME(pf_flags_t)
30 {
31     APICHAR Sign, LeftAlign, Alternate, PadZero;
32     int FieldLength, Precision;
33     APICHAR IntegerLength, IntegerDouble;
34     APICHAR WideString;
35     APICHAR Format;
36 } FUNC_NAME(pf_flags);
37
38 struct FUNC_NAME(_str_ctx) {
39     MSVCRT_size_t len;
40     APICHAR *buf;
41 };
42
43 static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str)
44 {
45     struct FUNC_NAME(_str_ctx) *out = ctx;
46
47     if(!out->buf)
48         return len;
49
50     if(out->len < len) {
51         memcpy(out->buf, str, out->len);
52         out->buf += out->len;
53         out->len = 0;
54         return -1;
55     }
56
57     memcpy(out->buf, str, len*sizeof(APICHAR));
58     out->buf += len;
59     return len;
60 }
61
62 static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val)
63 {
64     *val = 0;
65
66     while(isdigit(*fmt)) {
67         *val *= 10;
68         *val += *fmt++ - '0';
69     }
70
71     return fmt;
72 }
73
74 /* pf_fill: takes care of signs, alignment, zero and field padding */
75 static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
76         int len, FUNC_NAME(pf_flags) *flags, BOOL left)
77 {
78     int i, r = 0, written;
79
80     if(flags->Sign && !(flags->Format=='d' || flags->Format=='i'))
81         flags->Sign = 0;
82
83     if(left && flags->Sign) {
84         flags->FieldLength--;
85         if(flags->PadZero)
86             r = pf_puts(puts_ctx, 1, &flags->Sign);
87     }
88     written = r;
89
90     if((!left && flags->LeftAlign) || (left && !flags->LeftAlign)) {
91         APICHAR ch;
92
93         if(left && flags->PadZero)
94             ch = '0';
95         else
96             ch = ' ';
97
98         for(i=0; i<flags->FieldLength-len && r>=0; i++) {
99             r = pf_puts(puts_ctx, 1, &ch);
100             written += r;
101         }
102     }
103
104
105     if(r>=0 && left && flags->Sign && !flags->PadZero) {
106         r = pf_puts(puts_ctx, 1, &flags->Sign);
107         written += r;
108     }
109
110     return r>=0 ? written : r;
111 }
112
113 static inline int FUNC_NAME(pf_output_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
114         const MSVCRT_wchar_t *str, int len, MSVCRT_pthreadlocinfo locinfo)
115 {
116 #ifdef PRINTF_WIDE
117     return pf_puts(puts_ctx, len, str);
118 #else
119     LPSTR out;
120     int len_a = WideCharToMultiByte(locinfo->lc_codepage, 0, str, len, NULL, 0, NULL, NULL);
121
122     out = HeapAlloc(GetProcessHeap(), 0, len_a);
123     if(!out)
124         return -1;
125
126     WideCharToMultiByte(locinfo->lc_codepage, 0, str, len, out, len_a, NULL, NULL);
127     len = pf_puts(puts_ctx, len_a, out);
128     HeapFree(GetProcessHeap(), 0, out);
129     return len;
130 #endif
131 }
132
133 static inline int FUNC_NAME(pf_output_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
134         const char *str, int len, MSVCRT_pthreadlocinfo locinfo)
135 {
136 #ifdef PRINTF_WIDE
137     LPWSTR out;
138     int len_w = MultiByteToWideChar(locinfo->lc_codepage, 0, str, len, NULL, 0);
139
140     out = HeapAlloc(GetProcessHeap(), 0, len_w*sizeof(WCHAR));
141     if(!out)
142         return -1;
143
144     MultiByteToWideChar(locinfo->lc_codepage, 0, str, len, out, len_w);
145     len = pf_puts(puts_ctx, len_w, out);
146     HeapFree(GetProcessHeap(), 0, out);
147     return len;
148 #else
149     return pf_puts(puts_ctx, len, str);
150 #endif
151 }
152
153 static inline int FUNC_NAME(pf_output_format_wstr)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
154         const MSVCRT_wchar_t *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
155 {
156     int r, ret;
157
158     if(len < 0)
159         len = strlenW(str);
160
161     if(flags->Precision>=0 && flags->Precision<len)
162         len = flags->Precision;
163
164     r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
165     ret = r;
166     if(r >= 0) {
167         r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locinfo);
168         ret += r;
169     }
170     if(r >= 0) {
171         r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
172         ret += r;
173     }
174
175     return r>=0 ? ret : r;
176 }
177
178 static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
179         const char *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
180 {
181     int r, ret;
182
183     if(len < 0)
184         len = strlen(str);
185
186     if(flags->Precision>=0 && flags->Precision<len)
187         len = flags->Precision;
188
189     r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE);
190     ret = r;
191     if(r >= 0) {
192         r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locinfo);
193         ret += r;
194     }
195     if(r >= 0) {
196         r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE);
197         ret += r;
198     }
199
200     return r>=0 ? ret : r;
201 }
202
203 static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx,
204         const void *str, int len, FUNC_NAME(pf_flags) *flags, MSVCRT_pthreadlocinfo locinfo)
205 {
206 #ifdef PRINTF_WIDE
207     static const MSVCRT_wchar_t nullW[] = {'(','n','u','l','l',')',0};
208
209     if(!str)
210         return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, nullW, 6, flags, locinfo);
211 #else
212     if(!str)
213         return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locinfo);
214 #endif
215
216     if(flags->WideString || flags->IntegerLength=='l')
217         return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
218     if(flags->IntegerLength == 'h')
219         return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
220
221     if((flags->Format=='S' || flags->Format=='C') == (sizeof(APICHAR)==sizeof(MSVCRT_wchar_t)))
222         return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locinfo);
223     else
224         return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locinfo);
225 }
226
227 static inline void FUNC_NAME(pf_rebuild_format_string)(char *p, FUNC_NAME(pf_flags) *flags)
228 {
229     *p++ = '%';
230     if(flags->Sign)
231         *p++ = flags->Sign;
232     if(flags->LeftAlign)
233         *p++ = flags->LeftAlign;
234     if(flags->Alternate)
235         *p++ = flags->Alternate;
236     if(flags->PadZero)
237         *p++ = flags->PadZero;
238     if(flags->FieldLength) {
239         sprintf(p, "%d", flags->FieldLength);
240         p += strlen(p);
241     }
242     if(flags->Precision >= 0) {
243         sprintf(p, ".%d", flags->Precision);
244         p += strlen(p);
245     }
246     *p++ = flags->Format;
247     *p++ = 0;
248 }
249
250 /* pf_integer_conv:  prints x to buf, including alternate formats and
251    additional precision digits, but not field characters or the sign */
252 static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, int buf_len,
253         FUNC_NAME(pf_flags) *flags, LONGLONG x)
254 {
255     unsigned int base;
256     const char *digits;
257     int i, j, k;
258
259     if(flags->Format == 'o')
260         base = 8;
261     else if(flags->Format=='x' || flags->Format=='X')
262         base = 16;
263     else
264         base = 10;
265
266     if(flags->Format == 'X')
267         digits = "0123456789ABCDEFX";
268     else
269         digits = "0123456789abcdefx";
270
271     if(x<0 && (flags->Format=='d' || flags->Format=='i')) {
272         x = -x;
273         flags->Sign = '-';
274     }
275
276     i = 0;
277     if(x==0 && flags->Precision)
278         buf[i++] = '0';
279     else {
280         while(x != 0) {
281             j = (ULONGLONG)x%base;
282             x = (ULONGLONG)x/base;
283             buf[i++] = digits[j];
284         }
285     }
286     k = flags->Precision-i;
287     while(k-- > 0)
288         buf[i++] = '0';
289     if(flags->Alternate) {
290         if(base == 16) {
291             buf[i++] = digits[16];
292             buf[i++] = '0';
293         } else if(base==8 && buf[i-1]!='0')
294             buf[i++] = '0';
295     }
296
297     /* Adjust precision so pf_fill won't truncate the number later */
298     flags->Precision = i;
299
300     buf[i] = '\0';
301     j = 0;
302     while(--i > j) {
303         APICHAR tmp = buf[j];
304         buf[j] = buf[i];
305         buf[i] = tmp;
306         j++;
307     }
308 }
309
310 static inline void FUNC_NAME(pf_fixup_exponent)(char *buf)
311 {
312     char* tmp = buf;
313
314     while(tmp[0] && toupper(tmp[0])!='E')
315         tmp++;
316
317     if(tmp[0] && (tmp[1]=='+' || tmp[1]=='-') &&
318             isdigit(tmp[2]) && isdigit(tmp[3])) {
319         char final;
320
321         if (isdigit(tmp[4]))
322             return; /* Exponent already 3 digits */
323
324         tmp += 2;
325         final = tmp[2];
326         tmp[2] = tmp[1];
327         tmp[1] = tmp[0];
328         tmp[0] = '0';
329
330         if(final == '\0') {
331             tmp[3] = '\0';
332             if(buf[0] == ' ')
333                 memmove(buf, buf + 1, (tmp - buf) + 3);
334         }
335     }
336 }
337
338 int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt,
339         MSVCRT__locale_t locale, BOOL positional_params, BOOL invoke_invalid_param_handler,
340         args_clbk pf_args, void *args_ctx, __ms_va_list *valist)
341 {
342     MSVCRT_pthreadlocinfo locinfo;
343     const APICHAR *q, *p = fmt;
344     APICHAR buf[32];
345     int written = 0, pos, i;
346     FUNC_NAME(pf_flags) flags;
347
348     TRACE("Format is: %s\n", FUNC_NAME(debugstr)(fmt));
349
350     if(!locale)
351         locinfo = get_locinfo();
352     else
353         locinfo = locale->locinfo;
354
355     while(*p) {
356         /* output characters before '%' */
357         for(q=p; *q && *q!='%'; q++);
358         if(p != q) {
359             i = pf_puts(puts_ctx, q-p, p);
360             if(i < 0)
361                 return i;
362
363             written += i;
364             p = q;
365             continue;
366         }
367
368         /* *p == '%' here */
369         p++;
370
371         /* output a single '%' character */
372         if(*p == '%') {
373             i = pf_puts(puts_ctx, 1, p++);
374             if(i < 0)
375                 return i;
376
377             written += i;
378             continue;
379         }
380
381         /* check parameter position */
382         if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$')
383             p = q+1;
384         else
385             pos = -1;
386
387         /* parse the flags */
388         memset(&flags, 0, sizeof(flags));
389         while(*p) {
390             if(*p=='+' || *p==' ') {
391                 if(flags.Sign != '+')
392                     flags.Sign = *p;
393             } else if(*p == '-')
394                 flags.LeftAlign = *p;
395             else if(*p == '0')
396                 flags.PadZero = *p;
397             else if(*p == '#')
398                 flags.Alternate = *p;
399             else
400                 break;
401
402             p++;
403         }
404
405         /* parse the widh */
406         if(*p == '*') {
407             p++;
408             if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
409                 p = q+1;
410             else
411                 i = -1;
412
413             flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int;
414             if(flags.FieldLength < 0) {
415                 flags.LeftAlign = '-';
416                 flags.FieldLength = -flags.FieldLength;
417             }
418         } else while(isdigit(*p)) {
419             flags.FieldLength *= 10;
420             flags.FieldLength += *p++ - '0';
421         }
422
423         /* parse the precision */
424         flags.Precision = -1;
425         if(*p == '.') {
426             flags.Precision = 0;
427             p++;
428             if(*p == '*') {
429                 p++;
430                 if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$')
431                     p = q+1;
432                 else
433                     i = -1;
434
435                 flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int;
436             } else while(isdigit(*p)) {
437                 flags.Precision *= 10;
438                 flags.Precision += *p++ - '0';
439             }
440         }
441
442         /* parse argument size modifier */
443         while(*p) {
444             if(*p=='l' && *(p+1)=='l') {
445                 flags.IntegerDouble++;
446                 p += 2;
447             } else if(*p=='h' || *p=='l' || *p=='L') {
448                 flags.IntegerLength = *p;
449                 p++;
450             } else if(*p == 'I') {
451                 if(*(p+1)=='6' && *(p+2)=='4') {
452                     flags.IntegerDouble++;
453                     p += 3;
454                 } else if(*(p+1)=='3' && *(p+2)=='2')
455                     p += 3;
456                 else if(isdigit(*(p+1)) || !*(p+1))
457                     break;
458                 else
459                     p++;
460             } else if(*p == 'w')
461                 flags.WideString = *p++;
462             else if(*p == 'F')
463                 p++; /* ignore */
464             else
465                 break;
466         }
467
468         flags.Format = *p;
469
470         if(flags.Format == 's' || flags.Format == 'S') {
471             i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx,
472                     pf_args(args_ctx, pos, VT_PTR, valist).get_ptr,
473                     -1,  &flags, locinfo);
474         } else if(flags.Format == 'c' || flags.Format == 'C') {
475             int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int;
476
477             if((ch&0xff) != ch)
478                 FIXME("multibyte characters printing not supported\n");
479
480             i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locinfo);
481         } else if(flags.Format == 'p') {
482             flags.Format = 'X';
483             flags.PadZero = '0';
484             i = flags.Precision;
485             flags.Precision = 2*sizeof(void*);
486             FUNC_NAME(pf_integer_conv)(buf, sizeof(buf)/sizeof(APICHAR), &flags,
487                     pf_args(args_ctx, pos, VT_INT, valist).get_int);
488             flags.PadZero = 0;
489             flags.Precision = i;
490
491 #ifdef PRINTF_WIDE
492             i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locinfo);
493 #else
494             i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locinfo);
495 #endif
496         } else if(flags.Format == 'n') {
497             int *used;
498
499             if(!n_format_enabled) {
500                 MSVCRT_INVALID_PMT("\'n\' format specifier disabled");
501                 *MSVCRT__errno() = MSVCRT_EINVAL;
502                 return -1;
503             }
504
505             used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr;
506             *used = written;
507             i = 0;
508         } else if(flags.Format && strchr("diouxX", flags.Format)) {
509             APICHAR *tmp = buf;
510             int max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
511
512             if(max_len > sizeof(buf)/sizeof(APICHAR))
513                 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
514             if(!tmp)
515                 return -1;
516
517             if(flags.IntegerDouble)
518                 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, pf_args(args_ctx, pos,
519                             VT_I8, valist).get_longlong);
520             else if(flags.Format=='d' || flags.Format=='i')
521                 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, pf_args(args_ctx, pos,
522                             VT_INT, valist).get_int);
523             else
524                 FUNC_NAME(pf_integer_conv)(tmp, max_len, &flags, (unsigned)pf_args(
525                             args_ctx, pos, VT_INT, valist).get_int);
526
527 #ifdef PRINTF_WIDE
528             i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locinfo);
529 #else
530             i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locinfo);
531 #endif
532             if(tmp != buf)
533                 HeapFree(GetProcessHeap(), 0, tmp);
534         } else if(flags.Format && strchr("aeEfgG", flags.Format)) {
535             char fmt[20], buf_a[32], *tmp = buf_a, *decimal_point;
536             int max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
537
538             if(max_len > sizeof(buf_a))
539                 tmp = HeapAlloc(GetProcessHeap(), 0, max_len);
540             if(!tmp)
541                 return -1;
542
543             FUNC_NAME(pf_rebuild_format_string)(fmt, &flags);
544
545             sprintf(tmp, fmt, pf_args(args_ctx, pos, VT_R8, valist).get_double);
546             if(toupper(flags.Format)=='E' || toupper(flags.Format)=='G')
547                 FUNC_NAME(pf_fixup_exponent)(tmp);
548
549             decimal_point = strchr(tmp, '.');
550             if(decimal_point)
551                 *decimal_point = *locinfo->lconv->decimal_point;
552
553             i = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, tmp, strlen(tmp), locinfo);
554             if(tmp != buf_a)
555                 HeapFree(GetProcessHeap(), 0, tmp);
556         } else {
557             if(invoke_invalid_param_handler) {
558                 MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
559                 *MSVCRT__errno() = MSVCRT_EINVAL;
560                 return -1;
561             }
562
563             continue;
564         }
565
566         if(i < 0)
567             return i;
568         written += i;
569         p++;
570     }
571
572     return written;
573 }
574
575 #ifndef PRINTF_WIDE
576 enum types_clbk_flags {
577     TYPE_CLBK_VA_LIST = 1,
578     TYPE_CLBK_POSITIONAL = 2,
579     TYPE_CLBK_ERROR_POS = 4,
580     TYPE_CLBK_ERROR_TYPE = 8
581 };
582
583 /* This functions stores types of arguments. It uses args[0] internally */
584 static printf_arg arg_clbk_type(void *ctx, int pos, int type, __ms_va_list *valist)
585 {
586     static const printf_arg ret;
587     printf_arg *args = ctx;
588
589     if(pos == -1) {
590         args[0].get_int |= TYPE_CLBK_VA_LIST;
591         return ret;
592     } else
593         args[0].get_int |= TYPE_CLBK_POSITIONAL;
594
595     if(pos<1 || pos>MSVCRT__ARGMAX)
596         args[0].get_int |= TYPE_CLBK_ERROR_POS;
597     else if(args[pos].get_int && args[pos].get_int!=type)
598         args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
599     else
600         args[pos].get_int = type;
601
602     return ret;
603 }
604 #endif
605
606 static int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, __ms_va_list valist)
607 {
608     struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
609     printf_arg *args = args_ctx;
610     int i, j;
611
612     i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL, TRUE, FALSE,
613             arg_clbk_type, args_ctx, NULL);
614     if(i < 0)
615         return i;
616
617     if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
618         return 0;
619     if(args[0].get_int != TYPE_CLBK_POSITIONAL)
620         return -1;
621
622     for(i=MSVCRT__ARGMAX; i>0; i--)
623         if(args[i].get_int)
624             break;
625
626     for(j=1; j<=i; j++) {
627         switch(args[j].get_int) {
628         case VT_I8:
629             args[j].get_longlong = va_arg(valist, LONGLONG);
630             break;
631         case VT_INT:
632             args[j].get_int = va_arg(valist, int);
633             break;
634         case VT_R8:
635             args[j].get_double = va_arg(valist, double);
636             break;
637         case VT_PTR:
638             args[j].get_ptr = va_arg(valist, void*);
639             break;
640         default:
641             return -1;
642         }
643     }
644
645     return j;
646 }
647
648 #undef APICHAR
649 #undef CONVCHAR
650 #undef FUNC_NAME