1 /**************************************************************************
2  Copyright:
3       (C) 2009 - 2012  Alexander Shaduri <ashaduri 'at' gmail.com>
4  License: See LICENSE_zlib.txt file
5 ***************************************************************************/
6 /// \file
7 /// \author Alexander Shaduri
8 /// \ingroup hz
9 /// \weakgroup hz
10 /// @{
11 
12 #ifndef HZ_PORTABLE_SNPRINTF_H
13 #define HZ_PORTABLE_SNPRINTF_H
14 
15 #include "hz_config.h"  // feature macros
16 
17 
18 /**
19 \file
20 Compilation options:
21 - Define \c ENABLE_GLIB to 1 to enable glib-related code (portable ISO-compatible (v)snprintf).
22 
23 Keep in mind that these format types are non-portable (the first one is MS variant,
24 the second one is standard):
25 - %I64d, %lld (long long int),
26 - %I64u, %llu (unsigned long long int),
27 - %f, %Lf (long double; needs casting to double under non-ANSI mingw if using %f).
28 */
29 
30 
31 namespace hz {
32 
33 
34 // Note: There are macros, so they are not namespaced.
35 
36 
37 /// \def HAVE_PORTABLE_SNPRINTF_MS
38 /// If 1, portable_snprintf() and portable_vsnprintf() accept I64d, I64u.
39 /// Users are expected to check this macro to see if the format is supported.
40 
41 /// \def HAVE_PORTABLE_SNPRINTF_ISO
42 /// If 1, portable_snprintf() and portable_vsnprintf() accept lld, llu, Lf.
43 /// Users are expected to check this macro to see if the format is supported.
44 
45 
46 
47 /// \def portable_snprintf(buf, buf_size, ...)
48 /// snprintf() wrapper that always behaves according to ISO standard in terms of 0-termination.
49 /// Note that this is a macro, and therefore not namespaced.
50 /// Use it like this:
51 /// \code void portable_snprintf(char *str, size_t size, const char *format, ...); \endcode
52 
53 #if defined ENABLE_GLIB && ENABLE_GLIB
54 	#include <glib.h>  // g_snprintf
55 
56 	// It's ISO-compatible
57 	#define portable_snprintf(buf, buf_size, ...) \
58 		(void)g_snprintf((buf), (buf_size), __VA_ARGS__)
59 
60 	#define HAVE_PORTABLE_SNPRINTF_MS 0
61 	#define HAVE_PORTABLE_SNPRINTF_ISO 1
62 
63 #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO
64 	// mingw/ISO, all non-windows platforms.
65 	#include <cstdio>  // for cstdio
66 	#include <stdio.h>  // snprintf
67 
68 	#define portable_snprintf(buf, buf_size, ...) \
69 		(void)snprintf((buf), (buf_size), __VA_ARGS__)
70 
71 	// mingw/ISO has both the MS and ISO specifiers
72 	#if defined _WIN32 && defined __GNUC__
73 		#define HAVE_PORTABLE_SNPRINTF_MS 1
74 	#else
75 		#define HAVE_PORTABLE_SNPRINTF_MS 0
76 	#endif
77 
78 	#define HAVE_PORTABLE_SNPRINTF_ISO 1
79 
80 #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS
81 	#include <cstdio>  // for cstdio
82 	#include <stdio.h>  // _snprintf_s
83 
84 	// writes at most buf_size bytes, always including 0.
85 	#define portable_snprintf(buf, buf_size, ...) \
86 		(void)_snprintf_s((buf), (buf_size), _TRUNCATE, __VA_ARGS__)
87 
88 	#define HAVE_PORTABLE_SNPRINTF_MS 1
89 	#define HAVE_PORTABLE_SNPRINTF_ISO 0
90 
91 #elif defined HAVE__SNPRINTF && HAVE__SNPRINTF
92 	// Prefer snprintf() to _snprintf() if we're sure it's good.
93 	// Note that old versions of mingw used to define snprintf() as _snprintf(),
94 	// which is not proper.
95 
96 	#include <cstdio>  // for cstdio
97 	#include <stdio.h>  // _snprintf
98 
99 	// _snprintf() writes at most buf_size-1 bytes. the buffer should be zeroed out
100 	// beforehand.
101 	#define portable_snprintf(buf, buf_size, ...) \
102 		(void)((_snprintf((buf), (buf_size) - 1, __VA_ARGS__) == ((buf_size) - 1)) && ((buf)[(buf_size)-1] = '\0'))
103 
104 	#define HAVE_PORTABLE_SNPRINTF_MS 0
105 	#define HAVE_PORTABLE_SNPRINTF_ISO 1
106 
107 #else
108 	#error Cannot find suitable snprintf() implementation for portable_snprintf()
109 
110 #endif
111 
112 
113 
114 
115 /// \def portable_vsnprintf(buf, buf_size, ...)
116 /// vsnprintf() wrapper that always behaves according to ISO standard in terms of 0-termination.
117 /// Note that this is a macro, and therefore not namespaced.
118 /// Use it like this:
119 /// \code void portable_vsnprintf(char *str, size_t size, const char *format, va_list ap); \endcode
120 
121 #if defined ENABLE_GLIB && ENABLE_GLIB
122 	#include <glib.h>  // g_vsnprintf
123 
124 	// It's ISO-compatible
125 	#define portable_vsnprintf(buf, buf_size, format, ap) \
126 		(void)g_vsnprintf((buf), (buf_size), (format), (ap))
127 
128 	#define HAVE_PORTABLE_VSNPRINTF_MS 0
129 	#define HAVE_PORTABLE_VSNPRINTF_ISO 1
130 
131 #elif defined HAVE_ISO_STDIO && HAVE_ISO_STDIO
132 	#include <cstdio>  // for cstdio
133 	#include <stdio.h>  // vsnprintf (mingw, posix(?))
134 	#include <cstdarg>  // for cstdarg
135 	#include <stdarg.h>  // vsnprintf (gnu)
136 
137 	#define portable_vsnprintf(buf, buf_size, format, ap) \
138 		(void)vsnprintf((buf), (buf_size), (format), (ap))
139 
140 	// mingw/ISO has both the MS and ISO specifiers
141 	#if defined _WIN32 && defined __GNUC__
142 		#define HAVE_PORTABLE_VSNPRINTF_MS 1
143 	#else
144 		#define HAVE_PORTABLE_VSNPRINTF_MS 0
145 	#endif
146 	#define HAVE_PORTABLE_VSNPRINTF_ISO 1
147 
148 #elif defined HAVE_WIN_SE_FUNCS && HAVE_WIN_SE_FUNCS
149 	#include <cstdio>  // for cstdio
150 	#include <stdio.h>  // _vsnprintf_s
151 
152 	// writes at most buf_size bytes, always including 0.
153 	#define portable_vsnprintf(buf, buf_size, format, ap) \
154 		(void)vsnprintf_s((buf), (buf_size), _TRUNCATE, (format), (ap))
155 
156 	#define HAVE_PORTABLE_VSNPRINTF_MS 1
157 	#define HAVE_PORTABLE_VSNPRINTF_ISO 0
158 
159 #elif defined HAVE__VSNPRINTF && HAVE__VSNPRINTF
160 	// prefer snprintf() to _snprintf() if we're sure it's good.
161 
162 	#include <cstdio>  // for cstdio
163 	#include <stdio.h>  // _vsnprintf
164 	#include <cstdarg>  // for cstdarg
165 	#include <stdarg.h>  // _vsnprintf
166 
167 	// _vsnprintf() writes at most buf_size-1 bytes. the buffer should be zeroed out
168 	// beforehand.
169 	// Note that MS's vsnprintf() (which is the same as their _vsnprintf())
170 	// doesn't always write 0.
171 	// We use _vsnprintf() instead of vsnprintf() to avoid using the proper vsnprintf()
172 	// by chance (_vsnprintf() is always broken, vsnprintf() may not be).
173 	#define portable_vsnprintf(buf, buf_size, format, ap) \
174 		(void)((_vsnprintf((buf), (buf_size) - 1, (format), (ap)) == ((buf_size) - 1)) && ((buf)[(buf_size)-1] = '\0'))
175 
176 	#define HAVE_PORTABLE_VSNPRINTF_MS 1
177 	#define HAVE_PORTABLE_VSNPRINTF_ISO 0
178 
179 #else
180 	#error Cannot find suitable vsnprintf() implementation for portable_vsnprintf()
181 #endif
182 
183 
184 
185 
186 
187 }  // ns
188 
189 
190 
191 
192 
193 #endif
194 
195 /// @}
196