1 /* Formatted output to strings, using POSIX/XSI format strings with positions.
2 Copyright (C) 2003, 2006-2007, 2009-2011, 2018, 2020-2023 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 /* Specifications of the libintl_*printf functions. */
54 #include "libgnuintl.h"
55
56 /* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */
57 #ifndef EOVERFLOW
58 # define EOVERFLOW E2BIG
59 #endif
60
61 /* When building a DLL, we must export some functions. Note that because
62 the functions are only defined for binary backward compatibility, we
63 don't need to use __declspec(dllimport) in any case. */
64 #if HAVE_VISIBILITY && BUILDING_DLL
65 # define DLL_EXPORTED __attribute__((__visibility__("default")))
66 #elif defined _MSC_VER && BUILDING_DLL
67 /* When building with MSVC, exporting a symbol means that the object file
68 contains a "linker directive" of the form /EXPORT:symbol. This can be
69 inspected through the "objdump -s --section=.drectve FILE" or
70 "dumpbin /directives FILE" commands.
71 The symbols from this file should be exported if and only if the object
72 file gets included in a DLL. Libtool, on Windows platforms, defines
73 the C macro DLL_EXPORT (together with PIC) when compiling for a DLL
74 and does not define it when compiling an object file meant to be linked
75 statically into some executable. */
76 # if defined DLL_EXPORT
77 # define DLL_EXPORTED __declspec(dllexport)
78 # else
79 # define DLL_EXPORTED
80 # endif
81 #else
82 # define DLL_EXPORTED
83 #endif
84
85 #define STATIC static
86
87 /* You can enable this for debugging on Windows. But not in a release! */
88 #if 0
89 # define ENABLE_WCHAR_FALLBACK 1
90 #endif
91
92 /* This needs to be consistent with libgnuintl.in.h. */
93 #if defined __NetBSD__ || defined __BEOS__ || defined __CYGWIN__ || defined __MINGW32__
94 /* Don't break __attribute__((format(printf,M,N))).
95 This redefinition is only possible because the libc in NetBSD, Cygwin,
96 mingw does not have a function __printf__. */
97 # define libintl_printf __printf__
98 #endif
99
100 #if 0 /* not needed */
101
102 /* Define auxiliary functions declared in "printf-args.h". */
103 #include "printf-args.c"
104
105 /* Define auxiliary functions declared in "printf-parse.h". */
106 #include "printf-parse.c"
107
108 /* Define functions declared in "vasnprintf.h". */
109 #define vasnprintf _libintl_vasnprintf
110 #include "vasnprintf.c"
111 #define asnprintf _libintl_asnprintf
112 #include "asnprintf.c"
113
114 #else
115
116 /* Get the declaration of _libintl_vasnprintf. */
117 #include "vasnprintf.h"
118
119 #endif
120
121 /* Users don't expect libintl_fprintf to be less POSIX compliant
122 than the fprintf implementation provided by gnulib or - on mingw -
123 the one provided by mingw libs when __USE_MINGW_ANSI_STDIO is in
124 effect. */
125 #define USE_REPLACEMENT_CODE_ALWAYS 1
126
127 DLL_EXPORTED
128 int
129 libintl_vfprintf (FILE *stream, const char *format, va_list args)
130 {
131 #if !USE_REPLACEMENT_CODE_ALWAYS
132 if (strchr (format, '$') == NULL)
133 return vfprintf (stream, format, args);
134 else
135 #endif
136 {
137 size_t length;
138 char *result = _libintl_vasnprintf (NULL, &length, format, args);
139 int retval = -1;
140 if (result != NULL)
141 {
142 size_t written = fwrite (result, 1, length, stream);
143 free (result);
144 if (written == length)
145 {
146 if (length > INT_MAX)
147 errno = EOVERFLOW;
148 else
149 retval = length;
150 }
151 }
152 return retval;
153 }
154 }
155
156 DLL_EXPORTED
157 int
158 libintl_fprintf (FILE *stream, const char *format, ...)
159 {
160 va_list args;
161 int retval;
162
163 va_start (args, format);
164 retval = libintl_vfprintf (stream, format, args);
165 va_end (args);
166 return retval;
167 }
168
169 DLL_EXPORTED
170 int
171 libintl_vprintf (const char *format, va_list args)
172 {
173 return libintl_vfprintf (stdout, format, args);
174 }
175
176 DLL_EXPORTED
177 int
178 libintl_printf (const char *format, ...)
179 {
180 va_list args;
181 int retval;
182
183 va_start (args, format);
184 retval = libintl_vprintf (format, args);
185 va_end (args);
186 return retval;
187 }
188
189 DLL_EXPORTED
190 int
191 libintl_vsprintf (char *resultbuf, const char *format, va_list args)
192 {
193 #if !USE_REPLACEMENT_CODE_ALWAYS
194 if (strchr (format, '$') == NULL)
195 return vsprintf (resultbuf, format, args);
196 else
197 #endif
198 {
199 size_t length = (size_t) ~0 / (4 * sizeof (char));
200 char *result = _libintl_vasnprintf (resultbuf, &length, format, args);
201 if (result != resultbuf)
202 {
203 free (result);
204 return -1;
205 }
206 if (length > INT_MAX)
207 {
208 errno = EOVERFLOW;
209 return -1;
210 }
211 else
212 return length;
213 }
214 }
215
216 DLL_EXPORTED
217 int
218 libintl_sprintf (char *resultbuf, const char *format, ...)
219 {
220 va_list args;
221 int retval;
222
223 va_start (args, format);
224 retval = libintl_vsprintf (resultbuf, format, args);
225 va_end (args);
226 return retval;
227 }
228
229 #if HAVE_SNPRINTF
230
231 # if HAVE_DECL__SNPRINTF
232 /* Windows. The mingw function vsnprintf() has fewer bugs than the MSVCRT
233 function _vsnprintf(), so prefer that. */
234 # if defined __MINGW32__
235 # define system_vsnprintf vsnprintf
236 # else
237 # define system_vsnprintf _vsnprintf
238 # endif
239 # else
240 /* Unix. */
241 # define system_vsnprintf vsnprintf
242 # endif
243
244 DLL_EXPORTED
245 int
246 libintl_vsnprintf (char *resultbuf, size_t length, const char *format, va_list args)
247 {
248 # if !USE_REPLACEMENT_CODE_ALWAYS
249 if (strchr (format, '$') == NULL)
250 return system_vsnprintf (resultbuf, length, format, args);
251 else
252 # endif
253 {
254 size_t maxlength = length;
255 char *result = _libintl_vasnprintf (resultbuf, &length, format, args);
256 if (result == NULL)
257 return -1;
258 if (result != resultbuf)
259 {
260 if (maxlength > 0)
261 {
262 size_t pruned_length =
263 (length < maxlength ? length : maxlength - 1);
264 memcpy (resultbuf, result, pruned_length);
265 resultbuf[pruned_length] = '\0';
266 }
267 free (result);
268 }
269 if (length > INT_MAX)
270 {
271 errno = EOVERFLOW;
272 return -1;
273 }
274 else
275 return length;
276 }
277 }
278
279 DLL_EXPORTED
280 int
281 libintl_snprintf (char *resultbuf, size_t length, const char *format, ...)
282 {
283 va_list args;
284 int retval;
285
286 va_start (args, format);
287 retval = libintl_vsnprintf (resultbuf, length, format, args);
288 va_end (args);
289 return retval;
290 }
291
292 #endif
293
294 #if HAVE_ASPRINTF
295
296 DLL_EXPORTED
297 int
298 libintl_vasprintf (char **resultp, const char *format, va_list args)
299 {
300 size_t length;
301 char *result = _libintl_vasnprintf (NULL, &length, format, args);
302 if (result == NULL)
303 return -1;
304 if (length > INT_MAX)
305 {
306 free (result);
307 errno = EOVERFLOW;
308 return -1;
309 }
310 *resultp = result;
311 return length;
312 }
313
314 DLL_EXPORTED
315 int
316 libintl_asprintf (char **resultp, const char *format, ...)
317 {
318 va_list args;
319 int retval;
320
321 va_start (args, format);
322 retval = libintl_vasprintf (resultp, format, args);
323 va_end (args);
324 return retval;
325 }
326
327 #endif
328
329 #if HAVE_WPRINTF
330
331 #include <wchar.h>
332
333 #if 0 /* not needed */
334
335 /* Define auxiliary functions declared in "printf-args.h". */
336 #include "printf-args.c"
337
338 /* Define auxiliary functions declared in "wprintf-parse.h". */
339 #include "wprintf-parse.c"
340
341 /* Define functions declared in "vasnwprintf.h". */
342 #define vasnwprintf _libintl_vasnwprintf
343 #include "vasnwprintf.c"
344 #define asnwprintf _libintl_asnwprintf
345 #include "asnwprintf.c"
346
347 #else
348
349 /* Get the declaration of _libintl_vasnwprintf. */
350 #include "vasnwprintf.h"
351
352 #endif
353
354 # if HAVE_DECL__SNWPRINTF
355 /* Windows. The function vswprintf() has a different signature than
356 on Unix; we use the function _vsnwprintf() instead. */
357 # define system_vswprintf _vsnwprintf
358 # else
359 /* Unix. */
360 # define system_vswprintf vswprintf
361 # endif
362
363 DLL_EXPORTED
364 int
365 libintl_vfwprintf (FILE *stream, const wchar_t *format, va_list args)
366 {
367 # if !USE_REPLACEMENT_CODE_ALWAYS
368 if (wcschr (format, '$') == NULL)
369 return vfwprintf (stream, format, args);
370 else
371 # endif
372 {
373 size_t length;
374 wchar_t *result = _libintl_vasnwprintf (NULL, &length, format, args);
375 int retval = -1;
376 if (result != NULL)
377 {
378 size_t i;
379 for (i = 0; i < length; i++)
380 if (fputwc (result[i], stream) == WEOF)
381 break;
382 free (result);
383 if (i == length)
384 {
385 if (length > INT_MAX)
386 errno = EOVERFLOW;
387 else
388 retval = length;
389 }
390 }
391 return retval;
392 }
393 }
394
395 DLL_EXPORTED
396 int
397 libintl_fwprintf (FILE *stream, const wchar_t *format, ...)
398 {
399 va_list args;
400 int retval;
401
402 va_start (args, format);
403 retval = libintl_vfwprintf (stream, format, args);
404 va_end (args);
405 return retval;
406 }
407
408 DLL_EXPORTED
409 int
410 libintl_vwprintf (const wchar_t *format, va_list args)
411 {
412 return libintl_vfwprintf (stdout, format, args);
413 }
414
415 DLL_EXPORTED
416 int
417 libintl_wprintf (const wchar_t *format, ...)
418 {
419 va_list args;
420 int retval;
421
422 va_start (args, format);
423 retval = libintl_vwprintf (format, args);
424 va_end (args);
425 return retval;
426 }
427
428 DLL_EXPORTED
429 int
430 libintl_vswprintf (wchar_t *resultbuf, size_t length, const wchar_t *format, va_list args)
431 {
432 # if !USE_REPLACEMENT_CODE_ALWAYS
433 if (wcschr (format, '$') == NULL)
434 return system_vswprintf (resultbuf, length, format, args);
435 else
436 # endif
437 {
438 size_t maxlength = length;
439 wchar_t *result = _libintl_vasnwprintf (resultbuf, &length, format, args);
440 if (result == NULL)
441 return -1;
442 if (result != resultbuf)
443 {
444 if (maxlength > 0)
445 {
446 size_t pruned_length =
447 (length < maxlength ? length : maxlength - 1);
448 memcpy (resultbuf, result, pruned_length * sizeof (wchar_t));
449 resultbuf[pruned_length] = 0;
450 }
451 free (result);
452 /* Unlike vsnprintf, which has to return the number of character that
453 would have been produced if the resultbuf had been sufficiently
454 large, the vswprintf function has to return a negative value if
455 the resultbuf was not sufficiently large. */
456 if (length >= maxlength)
457 return -1;
458 }
459 if (length > INT_MAX)
460 {
461 errno = EOVERFLOW;
462 return -1;
463 }
464 else
465 return length;
466 }
467 }
468
469 DLL_EXPORTED
470 int
471 libintl_swprintf (wchar_t *resultbuf, size_t length, const wchar_t *format, ...)
472 {
473 va_list args;
474 int retval;
475
476 va_start (args, format);
477 retval = libintl_vswprintf (resultbuf, length, format, args);
478 va_end (args);
479 return retval;
480 }
481
482 #endif
483
484 #endif