1 | /* Test for user-defined types in vfprintf. |
2 | Copyright (C) 2017-2024 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 |
45 | my_va_arg_function (void *mem, va_list *ap) |
46 | { |
47 | if (test_verbose > 0) |
48 | printf (format: "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 |
56 | my_printf_function (FILE *fp, const struct printf_info *info, |
57 | const void *const *args) |
58 | { |
59 | if (test_verbose > 0) |
60 | printf (format: "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 (wc: L'{', stream: fp) < 0) |
74 | return -1; |
75 | } |
76 | else |
77 | { |
78 | if (fputc (c: '{', stream: 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 (wc: L',', stream: fp) < 0) |
97 | return -1; |
98 | } |
99 | else |
100 | { |
101 | if (fputc (c: ',', stream: 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 (stream: fp, format: 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 (wc: L'}', stream: fp) < 0) |
125 | return -1; |
126 | } |
127 | else |
128 | { |
129 | if (fputc (c: '}', stream: fp) < 0) |
130 | return -1; |
131 | } |
132 | ++printed; |
133 | } |
134 | return printed; |
135 | } |
136 | |
137 | static int |
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 (format: "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 (format: "info: %s return value: %zu\n" , __func__, nargs); |
166 | return nargs; |
167 | } |
168 | |
169 | static int |
170 | do_test (void) |
171 | { |
172 | user_type = register_printf_type (fct: my_va_arg_function); |
173 | if (test_verbose > 0) |
174 | printf (format: "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 (ptr: 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 (ptr: str); |
196 | |
197 | str = NULL; |
198 | TEST_VERIFY (asprintf_alias (&str, "%1$P %2$P %3$P %4$P %5$P %6$P" , |
199 | 1L, 1.0, |
200 | 2L, 2.0, |
201 | 3L, 3.0, |
202 | 4L, 4.0, |
203 | 5L, 6.0, |
204 | 6L, 6.0) |
205 | >= 0); |
206 | free (ptr: str); |
207 | |
208 | str = NULL; |
209 | TEST_VERIFY (asprintf_alias (&str, "%1$P %2$P %3$P %4$P %5$P %6$P" |
210 | "%7$P %8$P %9$P %10$P %11$P %12$P" , |
211 | 1L, 1.0, |
212 | 2L, 2.0, |
213 | 3L, 3.0, |
214 | 4L, 4.0, |
215 | 5L, 6.0, |
216 | 6L, 6.0, |
217 | 7L, 7.0, |
218 | 8L, 8.0, |
219 | 9L, 9.0, |
220 | 10L, 10.0, |
221 | 11L, 11.0, |
222 | 12L, 12.0) |
223 | >= 0); |
224 | free (ptr: str); |
225 | |
226 | str = NULL; |
227 | TEST_VERIFY (asprintf_alias (&str, "%1$P %2$P %3$P %4$P %5$P %6$P" |
228 | "%7$P %8$P %9$P %10$P %11$P %12$P" |
229 | "%13$P %14$P %15$P %16$P %17$P %18$P" , |
230 | 1L, 1.0, |
231 | 2L, 2.0, |
232 | 3L, 3.0, |
233 | 4L, 4.0, |
234 | 5L, 6.0, |
235 | 6L, 6.0, |
236 | 7L, 7.0, |
237 | 8L, 8.0, |
238 | 9L, 9.0, |
239 | 10L, 10.0, |
240 | 11L, 11.0, |
241 | 12L, 12.0, |
242 | 13L, 13.0, |
243 | 14L, 14.0, |
244 | 15L, 15.0, |
245 | 16L, 16.0, |
246 | 17L, 17.0, |
247 | 18L, 18.0) |
248 | >= 0); |
249 | free (ptr: str); |
250 | |
251 | str = NULL; |
252 | TEST_VERIFY (asprintf_alias (&str, "%1$P %2$P %3$P %4$P %5$P %6$P" |
253 | "%7$P %8$P %9$P %10$P %11$P %12$P" |
254 | "%13$P %14$P %15$P %16$P %17$P %18$P" |
255 | "%19$P %20$P %21$P %22$P %23$P %24$P" , |
256 | 1L, 1.0, |
257 | 2L, 2.0, |
258 | 3L, 3.0, |
259 | 4L, 4.0, |
260 | 5L, 6.0, |
261 | 6L, 6.0, |
262 | 7L, 7.0, |
263 | 8L, 8.0, |
264 | 9L, 9.0, |
265 | 10L, 10.0, |
266 | 11L, 11.0, |
267 | 12L, 12.0, |
268 | 13L, 13.0, |
269 | 14L, 14.0, |
270 | 15L, 15.0, |
271 | 16L, 16.0, |
272 | 17L, 17.0, |
273 | 18L, 18.0, |
274 | 19L, 19.0, |
275 | 20L, 20.0, |
276 | 21L, 21.0, |
277 | 22L, 22.0, |
278 | 23L, 23.0, |
279 | 24L, 24.0) |
280 | >= 0); |
281 | free (ptr: str); |
282 | |
283 | str = NULL; |
284 | TEST_VERIFY (asprintf_alias (&str, "%1$P %2$P %3$P %4$P %5$P %6$P" |
285 | "%7$P %8$P %9$P %10$P %11$P %12$P" |
286 | "%13$P %14$P %15$P %16$P %17$P %18$P" |
287 | "%19$P %20$P %21$P %22$P %23$P %24$P" |
288 | "%25$P %26$P %27$P %28$P %29$P %30$P" , |
289 | 1L, 1.0, |
290 | 2L, 2.0, |
291 | 3L, 3.0, |
292 | 4L, 4.0, |
293 | 5L, 6.0, |
294 | 6L, 6.0, |
295 | 7L, 7.0, |
296 | 8L, 8.0, |
297 | 9L, 9.0, |
298 | 10L, 10.0, |
299 | 11L, 11.0, |
300 | 12L, 12.0, |
301 | 13L, 13.0, |
302 | 14L, 14.0, |
303 | 15L, 15.0, |
304 | 16L, 16.0, |
305 | 17L, 17.0, |
306 | 18L, 18.0, |
307 | 19L, 19.0, |
308 | 20L, 20.0, |
309 | 21L, 21.0, |
310 | 22L, 22.0, |
311 | 23L, 23.0, |
312 | 24L, 34.0, |
313 | 25L, 25.0, |
314 | 26L, 26.0, |
315 | 27L, 27.0, |
316 | 28L, 28.0, |
317 | 29L, 29.0, |
318 | 30, 30.0) |
319 | >= 0); |
320 | free (ptr: str); |
321 | |
322 | str = NULL; |
323 | TEST_VERIFY (asprintf_alias (&str, "[[%1$P %1$P]]" , 123L, 457.0) >= 0); |
324 | TEST_COMPARE_STRING (str, "[[(123, 457.000000) (123, 457.000000)]]" ); |
325 | free (ptr: str); |
326 | |
327 | str = NULL; |
328 | TEST_VERIFY (asprintf_alias (&str, "[[%.1P]]" , 1L, 2.0) >= 0); |
329 | TEST_COMPARE_STRING (str, "[[{(1, 2.000000)}]]" ); |
330 | free (ptr: str); |
331 | |
332 | str = NULL; |
333 | TEST_VERIFY (asprintf_alias (&str, "[[%.2P]]" , 1L, 2.0, 3L, 4.0) >= 0); |
334 | TEST_COMPARE_STRING (str, "[[{(1, 2.000000),(3, 4.000000)}]]" ); |
335 | free (ptr: str); |
336 | |
337 | str = NULL; |
338 | TEST_VERIFY (asprintf_alias |
339 | (&str, "[[%.2P | %.3P]]" , |
340 | /* argument 1: */ 1L, 2.0, 3L, 4.0, |
341 | /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0) |
342 | >= 0); |
343 | TEST_COMPARE_STRING (str, |
344 | "[[" |
345 | "{(1, 2.000000),(3, 4.000000)}" |
346 | " | " |
347 | "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}" |
348 | "]]" ); |
349 | free (ptr: str); |
350 | |
351 | /* The following subtest fails due to bug 21534. */ |
352 | #if 0 |
353 | str = NULL; |
354 | TEST_VERIFY (asprintf_alias |
355 | (&str, "[[%1$.2P | %2$.3P | %1$.2P]]" , |
356 | /* argument 1: */ 1L, 2.0, 3L, 4.0, |
357 | /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0) |
358 | >= 0); |
359 | TEST_COMPARE_STRING (str, |
360 | "[[" |
361 | "{(1, 2.000000),(3, 4.000000)}" |
362 | " | " |
363 | "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}" |
364 | " | " |
365 | "{(1, 2.000000),(3, 4.000000)}" |
366 | "]]" ); |
367 | free (str); |
368 | #endif |
369 | |
370 | /* Wide variants of the tests above. */ |
371 | |
372 | wchar_t buf[200]; |
373 | TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%P]]" , 123L, 456.0) |
374 | >= 0); |
375 | TEST_COMPARE_STRING_WIDE (buf, L"[[(123, 456.000000)]]" ); |
376 | |
377 | TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%1$P %1$P]]" , |
378 | 123L, 457.0) >= 0); |
379 | TEST_COMPARE_STRING_WIDE (buf, L"[[(123, 457.000000) (123, 457.000000)]]" ); |
380 | |
381 | TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%.1P]]" , 1L, 2.0) >= 0); |
382 | TEST_COMPARE_STRING_WIDE (buf, L"[[{(1, 2.000000)}]]" ); |
383 | |
384 | TEST_VERIFY (swprintf (buf, array_length (buf), L"[[%.2P]]" , |
385 | 1L, 2.0, 3L, 4.0) >= 0); |
386 | TEST_COMPARE_STRING_WIDE (buf, L"[[{(1, 2.000000),(3, 4.000000)}]]" ); |
387 | |
388 | TEST_VERIFY (swprintf |
389 | (buf, array_length (buf), L"[[%.2P | %.3P]]" , |
390 | /* argument 1: */ 1L, 2.0, 3L, 4.0, |
391 | /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0) |
392 | >= 0); |
393 | TEST_COMPARE_STRING_WIDE (buf, |
394 | L"[[" |
395 | "{(1, 2.000000),(3, 4.000000)}" |
396 | " | " |
397 | "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}" |
398 | "]]" ); |
399 | |
400 | /* The following subtest fails due to bug 21534. */ |
401 | #if 0 |
402 | TEST_VERIFY (swprintf |
403 | (&buf, array_length (buf), L"[[%1$.2P | %2$.3P | %1$.2P]]" , |
404 | /* argument 1: */ 1L, 2.0, 3L, 4.0, |
405 | /* argument 2: */ 5L, 6.0, 7L, 8.0, 9L, 10.0) |
406 | >= 0); |
407 | TEST_COMPARE_STRING_WIDE (buf, |
408 | L"[[" |
409 | "{(1, 2.000000),(3, 4.000000)}" |
410 | " | " |
411 | "{(5, 6.000000),(7, 8.000000),(9, 10.000000)}" |
412 | " | " |
413 | "{(1, 2.000000),(3, 4.000000)}" |
414 | "]]" ); |
415 | #endif |
416 | |
417 | return 0; |
418 | } |
419 | |
420 | #include <support/test-driver.c> |
421 | |