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