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