msvcrt: Added sprintf_p_l implementation.
diff --git a/dlls/msvcr100/msvcr100.spec b/dlls/msvcr100/msvcr100.spec
index 6d7718c..0a4fa93 100644
--- a/dlls/msvcr100/msvcr100.spec
+++ b/dlls/msvcr100/msvcr100.spec
@@ -1119,7 +1119,7 @@
 @ cdecl _splitpath_s(str ptr long ptr long ptr long ptr long) msvcrt._splitpath_s
 @ stub _sprintf_l
 @ stub _sprintf_p
-@ stub _sprintf_p_l
+@ varargs _sprintf_p_l(ptr long str ptr) msvcrt._sprintf_p_l
 @ stub _sprintf_s_l
 @ varargs _sscanf_l(str str ptr) msvcrt._sscanf_l
 @ varargs _sscanf_s_l(str str ptr) msvcrt._sscanf_s_l
@@ -1174,7 +1174,7 @@
 @ stub _swprintf_c
 @ stub _swprintf_c_l
 @ stub _swprintf_p
-@ stub _swprintf_p_l
+@ varargs _swprintf_p_l(ptr long wstr ptr) msvcrt._swprintf_p_l
 @ stub _swprintf_s_l
 @ varargs _swscanf_l(wstr wstr ptr) msvcrt._swscanf_l
 @ varargs _swscanf_s_l(wstr wstr ptr) msvcrt._swscanf_s_l
@@ -1259,8 +1259,8 @@
 @ cdecl _vsnwprintf_s(ptr long long wstr ptr) msvcrt._vsnwprintf_s
 @ cdecl _vsnwprintf_s_l(ptr long long wstr ptr ptr) msvcrt._vsnwprintf_s_l
 @ stub _vsprintf_l
-@ stub _vsprintf_p
-@ stub _vsprintf_p_l
+@ cdecl _vsprintf_p(ptr long str ptr) msvcrt._vsprintf_p
+@ cdecl _vsprintf_p_l(ptr long str ptr ptr) msvcrt._vsprintf_p_l
 @ stub _vsprintf_s_l
 @ cdecl _vswprintf(ptr long wstr ptr) msvcrt._vswprintf
 @ cdecl _vswprintf_c(ptr long wstr ptr) msvcrt._vswprintf_c
diff --git a/dlls/msvcr80/msvcr80.spec b/dlls/msvcr80/msvcr80.spec
index fa2a271..d8a5ca3 100644
--- a/dlls/msvcr80/msvcr80.spec
+++ b/dlls/msvcr80/msvcr80.spec
@@ -973,7 +973,7 @@
 @ cdecl _splitpath_s(str ptr long ptr long ptr long ptr long) msvcrt._splitpath_s
 @ stub _sprintf_l
 @ stub _sprintf_p
-@ stub _sprintf_p_l
+@ varargs _sprintf_p_l(ptr long str ptr) msvcrt._sprintf_p_l
 @ stub _sprintf_s_l
 @ varargs _sscanf_l(str str ptr) msvcrt._sscanf_l
 @ varargs _sscanf_s_l(str str ptr) msvcrt._sscanf_s_l
@@ -1027,7 +1027,7 @@
 @ stub _swprintf
 @ stub _swprintf_c
 @ stub _swprintf_p
-@ stub _swprintf_p_l
+@ varargs _swprintf_p_l(ptr long wstr ptr) msvcrt._swprintf_p_l
 @ stub _swprintf_s_l
 @ varargs _swscanf_l(wstr wstr ptr) msvcrt._swscanf_l
 @ varargs _swscanf_s_l(wstr wstr ptr) msvcrt._swscanf_s_l
@@ -1112,8 +1112,8 @@
 @ cdecl _vsnwprintf_s(ptr long long wstr ptr) msvcrt._vsnwprintf_s
 @ cdecl _vsnwprintf_s_l(ptr long long wstr ptr ptr) msvcrt._vsnwprintf_s_l
 @ stub _vsprintf_l
-@ stub _vsprintf_p
-@ stub _vsprintf_p_l
+@ cdecl _vsprintf_p(ptr long str ptr) msvcrt._vsprintf_p
+@ cdecl _vsprintf_p_l(ptr long str ptr ptr) msvcrt._vsprintf_p_l
 @ stub _vsprintf_s_l
 @ cdecl _vswprintf(ptr long wstr ptr) msvcrt._vswprintf
 @ cdecl _vswprintf_c(ptr long wstr ptr) msvcrt._vswprintf_c
diff --git a/dlls/msvcr90/msvcr90.spec b/dlls/msvcr90/msvcr90.spec
index cbf4f74..56d8c2d 100644
--- a/dlls/msvcr90/msvcr90.spec
+++ b/dlls/msvcr90/msvcr90.spec
@@ -959,7 +959,7 @@
 @ cdecl _splitpath_s(str ptr long ptr long ptr long ptr long) msvcrt._splitpath_s
 @ stub _sprintf_l
 @ stub _sprintf_p
-@ stub _sprintf_p_l
+@ varargs _sprintf_p_l(ptr long str ptr) msvcrt._sprintf_p_l
 @ stub _sprintf_s_l
 @ varargs _sscanf_l(str str ptr) msvcrt._sscanf_l
 @ varargs _sscanf_s_l(str str ptr) msvcrt._sscanf_s_l
@@ -1014,7 +1014,7 @@
 @ stub _swprintf_c
 @ stub _swprintf_c_l
 @ stub _swprintf_p
-@ stub _swprintf_p_l
+@ varargs _swprintf_p_l(ptr long wstr ptr) msvcrt._swprintf_p_l
 @ stub _swprintf_s_l
 @ varargs _swscanf_l(wstr wstr ptr) msvcrt._swscanf_l
 @ varargs _swscanf_s_l(wstr wstr ptr) msvcrt._swscanf_s_l
@@ -1099,8 +1099,8 @@
 @ cdecl _vsnwprintf_s(ptr long long wstr ptr) msvcrt._vsnwprintf_s
 @ cdecl _vsnwprintf_s_l(ptr long long wstr ptr ptr) msvcrt._vsnwprintf_s_l
 @ stub _vsprintf_l
-@ stub _vsprintf_p
-@ stub _vsprintf_p_l
+@ cdecl _vsprintf_p(ptr long str ptr) msvcrt._vsprintf_p
+@ cdecl _vsprintf_p_l(ptr long str ptr ptr) msvcrt._vsprintf_p_l
 @ stub _vsprintf_s_l
 @ cdecl _vswprintf(ptr long wstr ptr) msvcrt._vswprintf
 @ cdecl _vswprintf_c(ptr long wstr ptr) msvcrt._vswprintf_c
diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h
index 4fcdb14..852fe92 100644
--- a/dlls/msvcrt/msvcrt.h
+++ b/dlls/msvcrt/msvcrt.h
@@ -916,6 +916,7 @@
 #define MSVCRT_CHECK_PMT(x)   ((x) || (MSVCRT_INVALID_PMT(0),FALSE))
 #endif
 
+#define MSVCRT__ARGMAX 100
 typedef int (*puts_clbk_a)(void*, int, const char*);
 typedef int (*puts_clbk_w)(void*, int, const MSVCRT_wchar_t*);
 typedef union _printf_arg
@@ -931,6 +932,7 @@
 int pf_printf_w(puts_clbk_w, void*, const MSVCRT_wchar_t*, MSVCRT__locale_t,
         BOOL, BOOL, args_clbk, void*, __ms_va_list);
 printf_arg arg_clbk_valist(void*, int, int, __ms_va_list*);
+printf_arg arg_clbk_positional(void*, int, int, __ms_va_list*);
 
 #define MSVCRT__OVERFLOW  3
 #define MSVCRT__UNDERFLOW 4
diff --git a/dlls/msvcrt/msvcrt.spec b/dlls/msvcrt/msvcrt.spec
index aaaca94..c1e7b75 100644
--- a/dlls/msvcrt/msvcrt.spec
+++ b/dlls/msvcrt/msvcrt.spec
@@ -901,7 +901,7 @@
 @ cdecl _splitpath(str ptr ptr ptr ptr)
 @ cdecl _splitpath_s(str ptr long ptr long ptr long ptr long)
 # stub _sprintf_l
-# stub _sprintf_p_l
+@ varargs _sprintf_p_l(ptr long str ptr) MSVCRT_sprintf_p_l
 # stub _sprintf_s_l
 @ varargs _sscanf_l(str str ptr) MSVCRT__sscanf_l
 @ varargs _sscanf_s_l(str str ptr) MSVCRT__sscanf_s_l
@@ -954,7 +954,7 @@
 # stub _swprintf
 # stub _swprintf_c
 # stub _swprintf_c_l
-# stub _swprintf_p_l
+@ varargs _swprintf_p_l(ptr long wstr ptr) MSVCRT_swprintf_p_l
 # stub _swprintf_s_l
 @ varargs _swscanf_l(wstr wstr ptr) MSVCRT__swscanf_l
 @ varargs _swscanf_s_l(wstr wstr ptr) MSVCRT__swscanf_s_l
@@ -1035,14 +1035,14 @@
 @ cdecl _vsnwprintf_s(ptr long long wstr ptr) MSVCRT_vsnwprintf_s
 @ cdecl _vsnwprintf_s_l(ptr long long wstr ptr ptr) MSVCRT_vsnwprintf_s_l
 # stub _vsprintf_l
-# stub _vsprintf_p
-# stub _vsprintf_p_l
+@ cdecl _vsprintf_p(ptr long str ptr) MSVCRT_vsprintf_p
+@ cdecl _vsprintf_p_l(ptr long str ptr ptr) MSVCRT_vsprintf_p_l
 # stub _vsprintf_s_l
 @ cdecl _vswprintf(ptr long wstr ptr) MSVCRT_vsnwprintf
 @ cdecl _vswprintf_c(ptr long wstr ptr) MSVCRT_vsnwprintf
 @ cdecl _vswprintf_c_l(ptr long wstr ptr ptr) MSVCRT_vsnwprintf_l
 @ cdecl _vswprintf_l(ptr long wstr ptr ptr) MSVCRT_vsnwprintf_l
-@ cdecl _vswprintf_p_l(ptr long wstr ptr ptr) MSVCRT_vsnwprintf_l
+@ cdecl _vswprintf_p_l(ptr long wstr ptr ptr) MSVCRT_vswprintf_p_l
 @ cdecl _vswprintf_s_l(ptr long wstr ptr ptr) MSVCRT_vswprintf_s_l
 # stub _vwprintf_l
 # stub _vwprintf_p
diff --git a/dlls/msvcrt/printf.h b/dlls/msvcrt/printf.h
index 2db9b6c..104dd9c 100644
--- a/dlls/msvcrt/printf.h
+++ b/dlls/msvcrt/printf.h
@@ -553,6 +553,79 @@
     return written;
 }
 
+#ifndef PRINTF_WIDE
+enum types_clbk_flags {
+    TYPE_CLBK_VA_LIST = 1,
+    TYPE_CLBK_POSITIONAL = 2,
+    TYPE_CLBK_ERROR_POS = 4,
+    TYPE_CLBK_ERROR_TYPE = 8
+};
+
+/* This functions stores types of arguments. It uses args[0] internally */
+static printf_arg arg_clbk_type(void *ctx, int pos, int type, __ms_va_list *valist)
+{
+    static const printf_arg ret;
+    printf_arg *args = ctx;
+
+    if(pos == -1) {
+        args[0].get_int |= TYPE_CLBK_VA_LIST;
+        return ret;
+    } else
+        args[0].get_int |= TYPE_CLBK_POSITIONAL;
+
+    if(pos<1 || pos>MSVCRT__ARGMAX)
+        args[0].get_int |= TYPE_CLBK_ERROR_POS;
+    else if(args[pos].get_int && args[pos].get_int!=type)
+        args[0].get_int |= TYPE_CLBK_ERROR_TYPE;
+    else
+        args[pos].get_int = type;
+
+    return ret;
+}
+#endif
+
+static int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, __ms_va_list valist)
+{
+    struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL};
+    printf_arg *args = args_ctx;
+    int i, j;
+
+    i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL, TRUE, FALSE,
+            arg_clbk_type, args_ctx, NULL);
+    if(i < 0)
+        return i;
+
+    if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST)
+        return 0;
+    if(args[0].get_int != TYPE_CLBK_POSITIONAL)
+        return -1;
+
+    for(i=MSVCRT__ARGMAX; i>0; i--)
+        if(args[i].get_int)
+            break;
+
+    for(j=1; j<=i; j++) {
+        switch(args[j].get_int) {
+        case VT_I8:
+            args[j].get_longlong = va_arg(valist, LONGLONG);
+            break;
+        case VT_INT:
+            args[j].get_int = va_arg(valist, int);
+            break;
+        case VT_R8:
+            args[j].get_double = va_arg(valist, double);
+            break;
+        case VT_PTR:
+            args[j].get_ptr = va_arg(valist, void*);
+            break;
+        default:
+            return -1;
+        }
+    }
+
+    return j;
+}
+
 #undef APICHAR
 #undef CONVCHAR
 #undef FUNC_NAME
diff --git a/dlls/msvcrt/tests/printf.c b/dlls/msvcrt/tests/printf.c
index a121533..eb34ad8 100644
--- a/dlls/msvcrt/tests/printf.c
+++ b/dlls/msvcrt/tests/printf.c
@@ -44,6 +44,7 @@
 static int (__cdecl *p__fcvt_s)(char *buffer, size_t length, double number,
                                 int ndigits, int *decpt, int *sign);
 static unsigned int (__cdecl *p__get_output_format)(void);
+static int (__cdecl *p__vsprintf_p)(char*, size_t, const char*, __ms_va_list);
 
 static void init( void )
 {
@@ -55,6 +56,7 @@
     p__ecvt_s = (void *)GetProcAddress(hmod, "_ecvt_s");
     p__fcvt_s = (void *)GetProcAddress(hmod, "_fcvt_s");
     p__get_output_format = (void *)GetProcAddress(hmod, "_get_output_format");
+    p__vsprintf_p = (void*)GetProcAddress(hmod, "_vsprintf_p");
 }
 
 static void test_sprintf( void )
@@ -1050,6 +1052,47 @@
     ok( !wcscmp(out1, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
 }
 
+static int __cdecl _vsprintf_p_wrapper(char *str, size_t sizeOfBuffer,
+                                 const char *format, ...)
+{
+    int ret;
+    __ms_va_list valist;
+    __ms_va_start(valist, format);
+    ret = p__vsprintf_p(str, sizeOfBuffer, format, valist);
+    __ms_va_end(valist);
+    return ret;
+}
+
+static void test_vsprintf_p(void)
+{
+    char buf[1024];
+    int ret;
+
+    if(!p__vsprintf_p) {
+        win_skip("vsprintf_p not available\n");
+        return;
+    }
+
+    ret = _vsprintf_p_wrapper(buf, sizeof(buf), "%s %d", "test", 1234);
+    ok(ret == 9, "ret = %d\n", ret);
+    ok(!memcmp(buf, "test 1234", 10), "buf = %s\n", buf);
+
+    ret = _vsprintf_p_wrapper(buf, sizeof(buf), "%1$d", 1234, "additional param");
+    ok(ret == 4, "ret = %d\n", ret);
+    ok(!memcmp(buf, "1234", 5), "buf = %s\n", buf);
+
+    ret = _vsprintf_p_wrapper(buf, sizeof(buf), "%2$s %1$d", 1234, "test");
+    ok(ret == 9, "ret = %d\n", ret);
+    ok(!memcmp(buf, "test 1234", 10), "buf = %s\n", buf);
+
+    ret = _vsprintf_p_wrapper(buf, sizeof(buf), "%2$*3$s %2$.*1$s", 2, "test", 3);
+    ok(ret == 7, "ret = %d\n", ret);
+    ok(!memcmp(buf, "test te", 8), "buf = %s\n", buf);
+
+    /* Following test invokes invalid parameter handler */
+    /* ret = _vsprintf_p_wrapper(buf, sizeof(buf), "%d %1$d", 1234); */
+}
+
 static void test__get_output_format(void)
 {
     unsigned int ret;
@@ -1078,5 +1121,6 @@
     test_vscprintf();
     test_vscwprintf();
     test_vsnwprintf_s();
+    test_vsprintf_p();
     test__get_output_format();
 }
diff --git a/dlls/msvcrt/wcs.c b/dlls/msvcrt/wcs.c
index 053d6e7..c0cdde1 100644
--- a/dlls/msvcrt/wcs.c
+++ b/dlls/msvcrt/wcs.c
@@ -437,6 +437,15 @@
 }
 
 /*********************************************************************
+ * arg_clbk_positional (INTERNAL)
+ */
+printf_arg arg_clbk_positional(void *ctx, int pos, int type, __ms_va_list *valist)
+{
+    printf_arg *args = ctx;
+    return args[pos];
+}
+
+/*********************************************************************
  *              _vsnprintf (MSVCRT.@)
  */
 int CDECL MSVCRT_vsnprintf( char *str, MSVCRT_size_t len,
@@ -790,6 +799,105 @@
 }
 
 /*********************************************************************
+ *		_vsprintf_p_l (MSVCRT.@)
+ */
+int CDECL MSVCRT_vsprintf_p_l(char *buffer, MSVCRT_size_t length, const char *format,
+        MSVCRT__locale_t locale, __ms_va_list args)
+{
+    static const char nullbyte = '\0';
+    printf_arg args_ctx[MSVCRT__ARGMAX+1];
+    struct _str_ctx_a puts_ctx = {length, buffer};
+    int ret;
+
+    memset(args_ctx, 0, sizeof(args_ctx));
+
+    ret = create_positional_ctx_a(args_ctx, format, args);
+    if(ret < 0) {
+        MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
+        *MSVCRT__errno() = MSVCRT_EINVAL;
+        return ret;
+    } else if(ret == 0)
+        ret = pf_printf_a(puts_clbk_str_a, &puts_ctx, format, locale, FALSE, TRUE,
+                arg_clbk_valist, NULL, args);
+    else
+        ret = pf_printf_a(puts_clbk_str_a, &puts_ctx, format, locale, TRUE, TRUE,
+                arg_clbk_positional, args_ctx, NULL);
+
+    puts_clbk_str_a(&puts_ctx, 1, &nullbyte);
+    return ret;
+}
+
+/*********************************************************************
+ *		_vsprintf_p (MSVCRT.@)
+ */
+int CDECL MSVCRT_vsprintf_p(char *buffer, MSVCRT_size_t length,
+        const char *format, __ms_va_list args)
+{
+    return MSVCRT_vsprintf_p_l(buffer, length, format, NULL, args);
+}
+
+/*********************************************************************
+ *		_sprintf_p_l (MSVCRT.@)
+ */
+int CDECL MSVCRT_sprintf_p_l(char *buffer, MSVCRT_size_t length,
+        const char *format, MSVCRT__locale_t locale, ...)
+{
+    __ms_va_list valist;
+    int r;
+
+    __ms_va_start(valist, locale);
+    r = MSVCRT_vsprintf_p_l(buffer, length, format, locale, valist);
+    __ms_va_end(valist);
+
+    return r;
+}
+
+/*********************************************************************
+ *		_vswprintf_p_l (MSVCRT.@)
+ */
+int CDECL MSVCRT_vswprintf_p_l(MSVCRT_wchar_t *buffer, MSVCRT_size_t length,
+        const MSVCRT_wchar_t *format, MSVCRT__locale_t locale, __ms_va_list args)
+{
+    static const MSVCRT_wchar_t nullbyte = '\0';
+    printf_arg args_ctx[MSVCRT__ARGMAX+1];
+    struct _str_ctx_w puts_ctx = {length, buffer};
+    int ret;
+
+    memset(args_ctx, 0, sizeof(args_ctx));
+
+    ret = create_positional_ctx_w(args_ctx, format, args);
+    if(ret < 0)  {
+        MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
+        *MSVCRT__errno() = MSVCRT_EINVAL;
+        return ret;
+    } else if(ret == 0)
+        ret = pf_printf_w(puts_clbk_str_w, &puts_ctx, format, locale, TRUE, TRUE,
+                arg_clbk_valist, NULL, args);
+    else
+        ret = pf_printf_w(puts_clbk_str_w, &puts_ctx, format, locale, TRUE, TRUE,
+                arg_clbk_positional, args_ctx, NULL);
+
+    puts_clbk_str_w(&puts_ctx, 1, &nullbyte);
+    return ret;
+}
+
+/*********************************************************************
+ *		_swprintf_p_l (MSVCRT.@)
+ */
+int CDECL MSVCRT_swprintf_p_l(MSVCRT_wchar_t *buffer, MSVCRT_size_t length,
+        const MSVCRT_wchar_t *format, MSVCRT__locale_t locale, ...)
+{
+    __ms_va_list valist;
+    int r;
+
+    __ms_va_start(valist, locale);
+    r = MSVCRT_vswprintf_p_l(buffer, length, format, locale, valist);
+    __ms_va_end(valist);
+
+    return r;
+}
+
+/*********************************************************************
  *		wcscoll (MSVCRT.@)
  */
 int CDECL MSVCRT_wcscoll( const MSVCRT_wchar_t* str1, const MSVCRT_wchar_t* str2 )
diff --git a/include/msvcrt/crtdefs.h b/include/msvcrt/crtdefs.h
index 5232e80..9685a99 100644
--- a/include/msvcrt/crtdefs.h
+++ b/include/msvcrt/crtdefs.h
@@ -97,6 +97,8 @@
 # endif
 #endif
 
+#define _ARGMAX 100
+
 #ifndef _MSVCRT_LONG_DEFINED
 #define _MSVCRT_LONG_DEFINED
 /* we need 32-bit longs even on 64-bit */