patch 9.0.1856: issues with formatting positional arguments

Problem:  issues with formatting positional arguments
Solution: fix them, add tests and documentation

closes: #12140
closes: #12985

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Christ van Willegen <cvwillegen@gmail.com>
Tentatively fix message_test. Check NULL ptr.
diff --git a/src/errors.h b/src/errors.h
index 1662043..1dfb9ab 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3511,5 +3511,7 @@
 	INIT(= N_("E1406: Member \"%s\": type mismatch, expected %s but got %s"));
 EXTERN char e_method_str_type_mismatch_expected_str_but_got_str[]
 	INIT(= N_("E1407: Member \"%s\": type mismatch, expected %s but got %s"));
+EXTERN char e_aptypes_is_null_str_nr[]
+	INIT(= "E1408: Internal error: ap_types or ap_types[idx] is NULL: %d: %s");
 
 // E1371 - E1399 unused
diff --git a/src/message_test.c b/src/message_test.c
index e5fd562..4d52889 100644
--- a/src/message_test.c
+++ b/src/message_test.c
@@ -40,6 +40,7 @@
 char *fmt_5S   = "%5S";
 char *fmt_06b  = "%06b";
 char *fmt_06pb = "%1$0.*2$b";
+char *fmt_06pb2 = "%2$0*1$b";
 char *fmt_212s = "%2$s %1$s %2$s";
 char *fmt_21s  = "%2$s %1$s";
 
@@ -442,6 +443,11 @@
 	assert(bsize == 0 || STRNCMP(buf, "deadbeef", bsize_int) == 0);
 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
 
+	n = vim_snprintf(buf, bsize, fmt_06pb2, 6, (uvarnumber_T)12);
+	assert(n == 6);
+	assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0);
+	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
 	n = vim_snprintf(buf, bsize, fmt_06pb, (uvarnumber_T)12, 6);
 	assert(n == 6);
 	assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0);
diff --git a/src/strings.c b/src/strings.c
index d1d82f4..cb044ec 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -2251,8 +2251,7 @@
  */
     static int
 format_typeof(
-    const char	*type,
-    int		usetvs UNUSED)
+    const char	*type)
 {
     // allowed values: \0, h, l, L
     char    length_modifier = '\0';
@@ -2285,18 +2284,6 @@
 	default: break;
     }
 
-# if defined(FEAT_EVAL)
-    if (usetvs)
-    {
-	switch (fmt_spec)
-	{
-	    case 'd': case 'u': case 'o': case 'x': case 'X':
-		if (length_modifier == '\0')
-		    length_modifier = 'L';
-	}
-    }
-# endif
-
     // get parameter value, do initial processing
     switch (fmt_spec)
     {
@@ -2330,7 +2317,7 @@
 	    if (fmt_spec == 'p')
 		return TYPE_POINTER;
 	    else if (fmt_spec == 'b' || fmt_spec == 'B')
-		return TYPE_UNSIGNEDINT;
+		return TYPE_UNSIGNEDLONGLONGINT;
 	    else if (fmt_spec == 'd')
 	    {
 		// signed
@@ -2379,7 +2366,7 @@
 format_typename(
     const char  *type)
 {
-    switch (format_typeof(type, FALSE))
+    switch (format_typeof(type))
     {
 	case TYPE_INT:
 	    return _(typename_int);
@@ -2467,7 +2454,7 @@
 	}
 	else
 	{
-	    if (format_typeof(type, FALSE) != format_typeof((*ap_types)[arg - 1], FALSE))
+	    if (format_typeof(type) != format_typeof((*ap_types)[arg - 1]))
 	    {
 		semsg(_( e_positional_arg_num_type_inconsistent_str_str), arg, format_typename(type), format_typename((*ap_types)[arg - 1]));
 		return FAIL;
@@ -2784,7 +2771,8 @@
     va_list	ap_start,
     va_list	*ap,
     int		*arg_idx,
-    int		*arg_cur)
+    int		*arg_cur,
+    const char	*fmt)
 {
     int		arg_min = 0;
 
@@ -2809,9 +2797,17 @@
 
     for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; ++*arg_cur)
     {
-	const char *p = ap_types[*arg_cur];
+	const char *p;
 
-	int fmt_type = format_typeof(p, TRUE);
+	if (ap_types == NULL || ap_types[*arg_cur] == NULL)
+	{
+	    semsg(e_aptypes_is_null_str_nr, fmt, *arg_cur);
+	    return;
+	}
+
+	p = ap_types[*arg_cur];
+
+	int fmt_type = format_typeof(p);
 
 	// get parameter value, do initial processing
 	switch (fmt_type)
@@ -3024,7 +3020,8 @@
 # if defined(FEAT_EVAL)
 		    tvs != NULL ? tv_nr(tvs, &arg_idx) :
 # endif
-			(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+			(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+				     &arg_cur, fmt),
 			va_arg(ap, int));
 
 		if (j >= 0)
@@ -3084,7 +3081,8 @@
 # if defined(FEAT_EVAL)
 			tvs != NULL ? tv_nr(tvs, &arg_idx) :
 # endif
-			    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+			    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+					 &arg_cur, fmt),
 			    va_arg(ap, int));
 
 		    if (j >= 0)
@@ -3157,7 +3155,8 @@
 # if defined(FEAT_EVAL)
 			    tvs != NULL ? tv_nr(tvs, &arg_idx) :
 # endif
-				(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+				(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+					     &arg_cur, fmt),
 				va_arg(ap, int));
 
 			// standard demands unsigned char
@@ -3172,7 +3171,8 @@
 # if defined(FEAT_EVAL)
 				tvs != NULL ? tv_str(tvs, &arg_idx, &tofree) :
 # endif
-				    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+				    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+						 &arg_cur, fmt),
 				    va_arg(ap, char *));
 
 		    if (str_arg == NULL)
@@ -3269,7 +3269,8 @@
 				 tvs != NULL ? (void *)tv_str(tvs, &arg_idx,
 									NULL) :
 # endif
-					(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+					(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+						     &arg_cur, fmt),
 					va_arg(ap, void *));
 
 			if (ptr_arg != NULL)
@@ -3282,7 +3283,8 @@
 				    tvs != NULL ?
 					   (uvarnumber_T)tv_nr(tvs, &arg_idx) :
 # endif
-					(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+					(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+						     &arg_cur, fmt),
 					va_arg(ap, uvarnumber_T));
 
 			if (bin_arg != 0)
@@ -3300,7 +3302,8 @@
 # if defined(FEAT_EVAL)
 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
 # endif
-					    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+					    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+							 &arg_cur, fmt),
 					    va_arg(ap, int));
 
 			    if (int_arg > 0)
@@ -3313,7 +3316,8 @@
 # if defined(FEAT_EVAL)
 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
 # endif
-					    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+					    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+							 &arg_cur, fmt),
 					    va_arg(ap, long int));
 
 			    if (long_arg > 0)
@@ -3326,7 +3330,8 @@
 # if defined(FEAT_EVAL)
 					tvs != NULL ? tv_nr(tvs, &arg_idx) :
 # endif
-					    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+					    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+							 &arg_cur, fmt),
 					    va_arg(ap, varnumber_T));
 
 			    if (llong_arg > 0)
@@ -3348,7 +3353,8 @@
 					    tvs != NULL ? (unsigned)
 							tv_nr(tvs, &arg_idx) :
 # endif
-						(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+						(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+							     &arg_cur, fmt),
 						va_arg(ap, unsigned int));
 
 				if (uint_arg != 0)
@@ -3360,7 +3366,8 @@
 					    tvs != NULL ? (unsigned long)
 							tv_nr(tvs, &arg_idx) :
 # endif
-						(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+						(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+							     &arg_cur, fmt),
 						va_arg(ap, unsigned long int));
 
 				if (ulong_arg != 0)
@@ -3372,7 +3379,8 @@
 					    tvs != NULL ? (uvarnumber_T)
 							tv_nr(tvs, &arg_idx) :
 # endif
-						(skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+						(skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+							     &arg_cur, fmt),
 						va_arg(ap, uvarnumber_T));
 
 				if (ullong_arg != 0)
@@ -3574,7 +3582,8 @@
 # if defined(FEAT_EVAL)
 			tvs != NULL ? tv_float(tvs, &arg_idx) :
 # endif
-			    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
+			    (skip_to_arg(ap_types, ap_start, &ap, &arg_idx,
+					 &arg_cur, fmt),
 			    va_arg(ap, double));
 
 		    abs_f = f < 0 ? -f : f;
diff --git a/src/version.c b/src/version.c
index a07a2bf..98964fb 100644
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1856,
+/**/
     1855,
 /**/
     1854,