1 /* Test for user-defined types in vfprintf.
2 Copyright (C) 2017-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 /* This test contains a printf format specifier, %P, with a custom
20 type which is a long/double pair. If a precision is specified,
21 this indicates the number of such pairs which constitute the
22 argument. */
23
24 #include <array_length.h>
25 #include <locale.h>
26 #include <printf.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <support/check.h>
31 #include <support/support.h>
32 #include <support/test-driver.h>
33 #include <wchar.h>
34
35 /* Initialized by do_test using register_printf_type. */
36 static int user_type;
37
38 struct two_argument
39 {
40 long i;
41 double d;
42 };
43
44 static void
my_va_arg_function(void * mem,va_list * ap)45 my_va_arg_function (void *mem, va_list *ap)
46 {
47 if (test_verbose > 0)
48 printf ("info: %s (%p) called\n", __func__, mem);
49
50 struct two_argument *pair = mem;
51 pair->i = va_arg (*ap, long);
52 pair->d = va_arg (*ap, double);
53 }
54
55 static int
my_printf_function(FILE * fp,const struct printf_info * info,const void * const * args)56 my_printf_function (FILE *fp, const struct printf_info *info,
57 const void *const *args)
58 {
59 if (test_verbose > 0)
60 printf ("info: %s (%p, %p, {%p}@%p) called for %%%lc (prec %d)\n",
61 __func__, fp, info, args[0], args, (wint_t) info->spec,
62 info->prec);
63
64 TEST_COMPARE (info->wide, fwide (fp, 0) > 0);
65
66 TEST_VERIFY (info->spec == 'P');
67 size_t nargs;
68 int printed;
69 if (info->prec >= 0)
70 {
71 if (info->wide)
72 {
73 if (fputwc (L'{', fp) < 0)
74 return -1;
75 }
76 else
77 {
78 if (fputc ('{', fp) < 0)
79 return -1;
80 }
81 nargs = info->prec;
82 printed = 1;
83 }
84 else
85 {
86 nargs = 1;
87 printed = 0;
88 }
89
90 for (size_t i = 0; i < nargs; ++i)
91 {
92 if (i != 0)
93 {
94 if (info->wide)
95 {
96 if (fputwc (L',', fp) < 0)
97 return -1;
98 }
99 else
100 {
101 if (fputc (',', fp) < 0)
102 return -1;
103 }
104 ++printed;
105 }
106
107 /* NB: Triple pointer indirection. ARGS is an array of void *,
108 and those pointers point to a pointer to the memory area
109 supplied to my_va_arg_function. */
110 struct two_argument *pair = *(void **) args[i];
111 int ret;
112 if (info->wide)
113 ret = fwprintf (fp, L"(%ld, %f)", pair->i, pair->d);
114 else
115 ret = fprintf (fp, "(%ld, %f)", pair->i, pair->d);
116 if (ret < 0)
117 return -1;
118 printed += ret;
119 }
120 if (info->prec >= 0)
121 {
122 if (info->wide)
123 {
124 if (fputwc (L'}', fp) < 0)
125 return -1;
126 }
127 else
128 {
129 if (fputc ('}', fp) < 0)
130 return -1;
131 }
132 ++printed;
133 }
134 return printed;
135 }
136
137 static int
my_arginfo_function(const struct printf_info * info,size_t n,int * argtypes,int * size)138 my_arginfo_function (const struct printf_info *info,
139 size_t n, int *argtypes, int *size)
140 {
141 /* Avoid recursion. */
142 if (info->spec != 'P')
143 return -1;
144 if (test_verbose > 0)
145 printf ("info: %s (%p, %zu, %p, %p) called for %%%lc (prec %d)\n",
146 __func__, info, n, argtypes, size, (wint_t) info->spec,
147 info->prec);
148
149 TEST_VERIFY_EXIT (n >= 1);
150 size_t nargs;
151 if (info->prec >= 0)
152 nargs = info->prec;
153 else
154 nargs = 1;
155
156 size_t to_fill = nargs;
157 if (to_fill > n)
158 to_fill = n;
159 for (size_t i = 0; i < to_fill; ++i)
160 {
161 argtypes[i] = user_type;
162 size[i] = sizeof (struct two_argument);
163 }
164 if (test_verbose > 0)
165 printf ("info: %s return value: %zu\n", __func__, nargs);
166 return nargs;
167 }
168
169 static int
do_test(void)170 do_test (void)
171 {
172 user_type = register_printf_type (my_va_arg_function);
173 if (test_verbose > 0)
174 printf ("info: allocated user type: %d\n", user_type);
175 TEST_VERIFY_EXIT (user_type >= PA_LAST);
176 TEST_VERIFY_EXIT (register_printf_specifier
177 ('P', my_printf_function, my_arginfo_function) >= 0);
178
179 /* Alias declaration for asprintf, to avoid the format string
180 attribute and the associated warning. */
181 #if __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1
182 extern int asprintf_alias (char **, const char *, ...) __asm__ ("__asprintfieee128");
183 #else
184 extern int asprintf_alias (char **, const char *, ...) __asm__ ("asprintf");
185 #endif
186 TEST_VERIFY (asprintf_alias == asprintf);
187 char *str = NULL;
188 TEST_VERIFY (asprintf_alias (&str, "[[%P]]", 123L, 456.0) >= 0);
189 TEST_COMPARE_STRING (str, "[[(123, 456.000000)]]");
190 free (str);
191
192 str = NULL;
193 TEST_VERIFY (asprintf_alias (&str, "[[%1$P %1$P]]", 123L, 457.0) >= 0);
194 TEST_COMPARE_STRING (str, "[[(123, 457.000000) (123, 457.000000)]]");
195 free (str);
196
197 str = NULL;
198 TEST_VERIFY (asprintf_alias (&str, "[[%.1P]]", 1L, 2.0) >= 0);
199 TEST_COMPARE_STRING (str, "[[{(1, 2.000000)}]]");
200 free (str);
201
202 str = NULL;
203 TEST_VERIFY (asprintf_alias (&str, "[[%.2P]]", 1L, 2.0, 3L, 4.0) >= 0);
204 TEST_COMPARE_STRING (str, "[[{(1, 2.000000),(3, 4.000000)}]]");
205 free (str);
206
207 str = NULL;
208 TEST_VERIFY (asprintf_alias
209 (&str, "[[%.2P | %.3P]]",
210 /* argument 1: */ 1L, 2.0, 3L, 4.0,
211 /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0)
212 >= 0);
213 TEST_COMPARE_STRING (str,
214 "[["
215 "{(1, 2.000000),(3, 4.000000)}"
216 " | "
217 "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}"
218 "]]");
219 free (str);
220
221 /* The following subtest fails due to bug 21534. */
222 #if 0
223 str = NULL;
224 TEST_VERIFY (asprintf_alias
225 (&str, "[[%1$.2P | %2$.3P | %1$.2P]]",
226 /* argument 1: */ 1L, 2.0, 3L, 4.0,
227 /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0)
228 >= 0);
229 TEST_COMPARE_STRING (str,
230 "[["
231 "{(1, 2.000000),(3, 4.000000)}"
232 " | "
233 "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}"
234 " | "
235 "{(1, 2.000000),(3, 4.000000)}"
236 "]]");
237 free (str);
238 #endif
239
240 /* Wide variants of the tests above. */
241
242 wchar_t buf[200];
243 TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%P]]", 123L, 456.0)
244 >= 0);
245 TEST_COMPARE_STRING_WIDE (buf, L"[[(123, 456.000000)]]");
246
247 TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%1$P %1$P]]",
248 123L, 457.0) >= 0);
249 TEST_COMPARE_STRING_WIDE (buf, L"[[(123, 457.000000) (123, 457.000000)]]");
250
251 TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%.1P]]", 1L, 2.0) >= 0);
252 TEST_COMPARE_STRING_WIDE (buf, L"[[{(1, 2.000000)}]]");
253
254 TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%.2P]]",
255 1L, 2.0, 3L, 4.0) >= 0);
256 TEST_COMPARE_STRING_WIDE (buf, L"[[{(1, 2.000000),(3, 4.000000)}]]");
257
258 TEST_VERIFY (swprintf
259 (buf, array_length (buf), L"[[%.2P | %.3P]]",
260 /* argument 1: */ 1L, 2.0, 3L, 4.0,
261 /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0)
262 >= 0);
263 TEST_COMPARE_STRING_WIDE (buf,
264 L"[["
265 "{(1, 2.000000),(3, 4.000000)}"
266 " | "
267 "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}"
268 "]]");
269
270 /* The following subtest fails due to bug 21534. */
271 #if 0
272 TEST_VERIFY (swprintf
273 (&buf, array_length (buf), L"[[%1$.2P | %2$.3P | %1$.2P]]",
274 /* argument 1: */ 1L, 2.0, 3L, 4.0,
275 /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0)
276 >= 0);
277 TEST_COMPARE_STRING_WIDE (buf,
278 L"[["
279 "{(1, 2.000000),(3, 4.000000)}"
280 " | "
281 "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}"
282 " | "
283 "{(1, 2.000000),(3, 4.000000)}"
284 "]]");
285 #endif
286
287 return 0;
288 }
289
290 #include <support/test-driver.c>
291