1 /* Formatted output to strings, using POSIX/XSI format strings with positions.
2 Copyright (C) 2003, 2006-2007, 2009-2011, 2018, 2020 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program 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
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #ifdef __GNUC__
23 # define alloca __builtin_alloca
24 # define HAVE_ALLOCA 1
25 #else
26 # ifdef _MSC_VER
27 # include <malloc.h>
28 # define alloca _alloca
29 # else
30 # if defined HAVE_ALLOCA_H || defined _LIBC
31 # include <alloca.h>
32 # else
33 # ifdef _AIX
34 #pragma alloca
35 # else
36 # ifndef alloca
37 char *alloca ();
38 # endif
39 # endif
40 # endif
41 # endif
42 #endif
43
44 #include <stdio.h>
45
46 #if !HAVE_POSIX_PRINTF
47
48 #include <errno.h>
49 #include <limits.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 /* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */
54 #ifndef EOVERFLOW
55 # define EOVERFLOW E2BIG
56 #endif
57
58 /* When building a DLL, we must export some functions. Note that because
59 the functions are only defined for binary backward compatibility, we
60 don't need to use __declspec(dllimport) in any case. */
61 #if HAVE_VISIBILITY && BUILDING_DLL
62 # define DLL_EXPORTED __attribute__((__visibility__("default")))
63 #elif defined _MSC_VER && BUILDING_DLL
64 # define DLL_EXPORTED __declspec(dllexport)
65 #else
66 # define DLL_EXPORTED
67 #endif
68
69 #define STATIC static
70
71 /* You can enable this for debugging on Windows. But not in a release! */
72 #if 0
73 # define ENABLE_WCHAR_FALLBACK 1
74 #endif
75
76 /* This needs to be consistent with libgnuintl.in.h. */
77 #if defined __NetBSD__ || defined __BEOS__ || defined __CYGWIN__ || defined __MINGW32__
78 /* Don't break __attribute__((format(printf,M,N))).
79 This redefinition is only possible because the libc in NetBSD, Cygwin,
80 mingw does not have a function __printf__. */
81 # define libintl_printf __printf__
82 #endif
83
84 /* Define auxiliary functions declared in "printf-args.h". */
85 #include "printf-args.c"
86
87 /* Define auxiliary functions declared in "printf-parse.h". */
88 #include "printf-parse.c"
89
90 /* Define functions declared in "vasnprintf.h". */
91 #define vasnprintf libintl_vasnprintf
92 #include "vasnprintf.c"
93 #if 0 /* not needed */
94 #define asnprintf libintl_asnprintf
95 #include "asnprintf.c"
96 #endif
97
98 /* Users don't expect libintl_fprintf to be less POSIX compliant
99 than the fprintf implementation provided by gnulib or - on mingw -
100 the one provided by mingw libs when __USE_MINGW_ANSI_STDIO is in
101 effect. */
102 #define USE_REPLACEMENT_CODE_ALWAYS 1
103
104 DLL_EXPORTED
105 int
libintl_vfprintf(FILE * stream,const char * format,va_list args)106 libintl_vfprintf (FILE *stream, const char *format, va_list args)
107 {
108 #if !USE_REPLACEMENT_CODE_ALWAYS
109 if (strchr (format, '$') == NULL)
110 return vfprintf (stream, format, args);
111 else
112 #endif
113 {
114 size_t length;
115 char *result = libintl_vasnprintf (NULL, &length, format, args);
116 int retval = -1;
117 if (result != NULL)
118 {
119 size_t written = fwrite (result, 1, length, stream);
120 free (result);
121 if (written == length)
122 {
123 if (length > INT_MAX)
124 errno = EOVERFLOW;
125 else
126 retval = length;
127 }
128 }
129 return retval;
130 }
131 }
132
133 DLL_EXPORTED
134 int
libintl_fprintf(FILE * stream,const char * format,...)135 libintl_fprintf (FILE *stream, const char *format, ...)
136 {
137 va_list args;
138 int retval;
139
140 va_start (args, format);
141 retval = libintl_vfprintf (stream, format, args);
142 va_end (args);
143 return retval;
144 }
145
146 DLL_EXPORTED
147 int
libintl_vprintf(const char * format,va_list args)148 libintl_vprintf (const char *format, va_list args)
149 {
150 return libintl_vfprintf (stdout, format, args);
151 }
152
153 DLL_EXPORTED
154 int
libintl_printf(const char * format,...)155 libintl_printf (const char *format, ...)
156 {
157 va_list args;
158 int retval;
159
160 va_start (args, format);
161 retval = libintl_vprintf (format, args);
162 va_end (args);
163 return retval;
164 }
165
166 DLL_EXPORTED
167 int
libintl_vsprintf(char * resultbuf,const char * format,va_list args)168 libintl_vsprintf (char *resultbuf, const char *format, va_list args)
169 {
170 #if !USE_REPLACEMENT_CODE_ALWAYS
171 if (strchr (format, '$') == NULL)
172 return vsprintf (resultbuf, format, args);
173 else
174 #endif
175 {
176 size_t length = (size_t) ~0 / (4 * sizeof (char));
177 char *result = libintl_vasnprintf (resultbuf, &length, format, args);
178 if (result != resultbuf)
179 {
180 free (result);
181 return -1;
182 }
183 if (length > INT_MAX)
184 {
185 errno = EOVERFLOW;
186 return -1;
187 }
188 else
189 return length;
190 }
191 }
192
193 DLL_EXPORTED
194 int
libintl_sprintf(char * resultbuf,const char * format,...)195 libintl_sprintf (char *resultbuf, const char *format, ...)
196 {
197 va_list args;
198 int retval;
199
200 va_start (args, format);
201 retval = libintl_vsprintf (resultbuf, format, args);
202 va_end (args);
203 return retval;
204 }
205
206 #if HAVE_SNPRINTF
207
208 # if HAVE_DECL__SNPRINTF
209 /* Windows. The mingw function vsnprintf() has fewer bugs than the MSVCRT
210 function _vsnprintf(), so prefer that. */
211 # if defined __MINGW32__
212 # define system_vsnprintf vsnprintf
213 # else
214 # define system_vsnprintf _vsnprintf
215 # endif
216 # else
217 /* Unix. */
218 # define system_vsnprintf vsnprintf
219 # endif
220
221 DLL_EXPORTED
222 int
libintl_vsnprintf(char * resultbuf,size_t length,const char * format,va_list args)223 libintl_vsnprintf (char *resultbuf, size_t length, const char *format, va_list args)
224 {
225 # if !USE_REPLACEMENT_CODE_ALWAYS
226 if (strchr (format, '$') == NULL)
227 return system_vsnprintf (resultbuf, length, format, args);
228 else
229 # endif
230 {
231 size_t maxlength = length;
232 char *result = libintl_vasnprintf (resultbuf, &length, format, args);
233 if (result == NULL)
234 return -1;
235 if (result != resultbuf)
236 {
237 if (maxlength > 0)
238 {
239 size_t pruned_length =
240 (length < maxlength ? length : maxlength - 1);
241 memcpy (resultbuf, result, pruned_length);
242 resultbuf[pruned_length] = '\0';
243 }
244 free (result);
245 }
246 if (length > INT_MAX)
247 {
248 errno = EOVERFLOW;
249 return -1;
250 }
251 else
252 return length;
253 }
254 }
255
256 DLL_EXPORTED
257 int
libintl_snprintf(char * resultbuf,size_t length,const char * format,...)258 libintl_snprintf (char *resultbuf, size_t length, const char *format, ...)
259 {
260 va_list args;
261 int retval;
262
263 va_start (args, format);
264 retval = libintl_vsnprintf (resultbuf, length, format, args);
265 va_end (args);
266 return retval;
267 }
268
269 #endif
270
271 #if HAVE_ASPRINTF
272
273 DLL_EXPORTED
274 int
libintl_vasprintf(char ** resultp,const char * format,va_list args)275 libintl_vasprintf (char **resultp, const char *format, va_list args)
276 {
277 size_t length;
278 char *result = libintl_vasnprintf (NULL, &length, format, args);
279 if (result == NULL)
280 return -1;
281 if (length > INT_MAX)
282 {
283 free (result);
284 errno = EOVERFLOW;
285 return -1;
286 }
287 *resultp = result;
288 return length;
289 }
290
291 DLL_EXPORTED
292 int
libintl_asprintf(char ** resultp,const char * format,...)293 libintl_asprintf (char **resultp, const char *format, ...)
294 {
295 va_list args;
296 int retval;
297
298 va_start (args, format);
299 retval = libintl_vasprintf (resultp, format, args);
300 va_end (args);
301 return retval;
302 }
303
304 #endif
305
306 #if HAVE_FWPRINTF
307
308 #include <wchar.h>
309
310 #define WIDE_CHAR_VERSION 1
311
312 #include "wprintf-parse.h"
313 /* Define auxiliary functions declared in "wprintf-parse.h". */
314 #define CHAR_T wchar_t
315 #define DIRECTIVE wchar_t_directive
316 #define DIRECTIVES wchar_t_directives
317 #define PRINTF_PARSE wprintf_parse
318 #include "printf-parse.c"
319
320 /* Define functions declared in "vasnprintf.h". */
321 #define vasnwprintf libintl_vasnwprintf
322 #include "vasnprintf.c"
323 #if 0 /* not needed */
324 #define asnwprintf libintl_asnwprintf
325 #include "asnprintf.c"
326 #endif
327
328 # if HAVE_DECL__SNWPRINTF
329 /* Windows. The function vswprintf() has a different signature than
330 on Unix; we use the function _vsnwprintf() instead. */
331 # define system_vswprintf _vsnwprintf
332 # else
333 /* Unix. */
334 # define system_vswprintf vswprintf
335 # endif
336
337 DLL_EXPORTED
338 int
libintl_vfwprintf(FILE * stream,const wchar_t * format,va_list args)339 libintl_vfwprintf (FILE *stream, const wchar_t *format, va_list args)
340 {
341 # if !USE_REPLACEMENT_CODE_ALWAYS
342 if (wcschr (format, '$') == NULL)
343 return vfwprintf (stream, format, args);
344 else
345 # endif
346 {
347 size_t length;
348 wchar_t *result = libintl_vasnwprintf (NULL, &length, format, args);
349 int retval = -1;
350 if (result != NULL)
351 {
352 size_t i;
353 for (i = 0; i < length; i++)
354 if (fputwc (result[i], stream) == WEOF)
355 break;
356 free (result);
357 if (i == length)
358 {
359 if (length > INT_MAX)
360 errno = EOVERFLOW;
361 else
362 retval = length;
363 }
364 }
365 return retval;
366 }
367 }
368
369 DLL_EXPORTED
370 int
libintl_fwprintf(FILE * stream,const wchar_t * format,...)371 libintl_fwprintf (FILE *stream, const wchar_t *format, ...)
372 {
373 va_list args;
374 int retval;
375
376 va_start (args, format);
377 retval = libintl_vfwprintf (stream, format, args);
378 va_end (args);
379 return retval;
380 }
381
382 DLL_EXPORTED
383 int
libintl_vwprintf(const wchar_t * format,va_list args)384 libintl_vwprintf (const wchar_t *format, va_list args)
385 {
386 return libintl_vfwprintf (stdout, format, args);
387 }
388
389 DLL_EXPORTED
390 int
libintl_wprintf(const wchar_t * format,...)391 libintl_wprintf (const wchar_t *format, ...)
392 {
393 va_list args;
394 int retval;
395
396 va_start (args, format);
397 retval = libintl_vwprintf (format, args);
398 va_end (args);
399 return retval;
400 }
401
402 DLL_EXPORTED
403 int
libintl_vswprintf(wchar_t * resultbuf,size_t length,const wchar_t * format,va_list args)404 libintl_vswprintf (wchar_t *resultbuf, size_t length, const wchar_t *format, va_list args)
405 {
406 # if !USE_REPLACEMENT_CODE_ALWAYS
407 if (wcschr (format, '$') == NULL)
408 return system_vswprintf (resultbuf, length, format, args);
409 else
410 # endif
411 {
412 size_t maxlength = length;
413 wchar_t *result = libintl_vasnwprintf (resultbuf, &length, format, args);
414 if (result == NULL)
415 return -1;
416 if (result != resultbuf)
417 {
418 if (maxlength > 0)
419 {
420 size_t pruned_length =
421 (length < maxlength ? length : maxlength - 1);
422 memcpy (resultbuf, result, pruned_length * sizeof (wchar_t));
423 resultbuf[pruned_length] = 0;
424 }
425 free (result);
426 /* Unlike vsnprintf, which has to return the number of character that
427 would have been produced if the resultbuf had been sufficiently
428 large, the vswprintf function has to return a negative value if
429 the resultbuf was not sufficiently large. */
430 if (length >= maxlength)
431 return -1;
432 }
433 if (length > INT_MAX)
434 {
435 errno = EOVERFLOW;
436 return -1;
437 }
438 else
439 return length;
440 }
441 }
442
443 DLL_EXPORTED
444 int
libintl_swprintf(wchar_t * resultbuf,size_t length,const wchar_t * format,...)445 libintl_swprintf (wchar_t *resultbuf, size_t length, const wchar_t *format, ...)
446 {
447 va_list args;
448 int retval;
449
450 va_start (args, format);
451 retval = libintl_vswprintf (resultbuf, length, format, args);
452 va_end (args);
453 return retval;
454 }
455
456 #endif
457
458 #endif
459