Lely core libraries 2.3.4
print.c
Go to the documentation of this file.
1
24#include "util.h"
25
26#if !LELY_NO_STDIO
27
28#define LELY_UTIL_PRINT_INLINE extern inline
29#include <lely/libc/stdio.h>
30#include <lely/libc/uchar.h>
31#include <lely/util/lex.h>
32#include <lely/util/print.h>
33
34#include <assert.h>
35#include <float.h>
36#include <stdint.h>
37#if __STDC_NO_VLA__
38#include <stdlib.h>
39#endif
40#include <string.h>
41
42size_t
43print_fmt(char **pbegin, char *end, const char *format, ...)
44{
45 va_list ap;
46 va_start(ap, format);
47 size_t chars = vprint_fmt(pbegin, end, format, ap);
48 va_end(ap);
49 return chars;
50}
51
52size_t
53vprint_fmt(char **pbegin, char *end, const char *format, va_list ap)
54{
55#if __STDC_NO_VLA__
56 if (pbegin && *pbegin && (!end || *pbegin < end)) {
57 char *buf = NULL;
58 va_list aq;
59 va_copy(aq, ap);
60 int chars = vasprintf(&buf, format, aq);
61 va_end(aq);
62 if (chars < 0)
63 return 0;
64 memcpy(*pbegin, buf, end ? MIN(end - *pbegin, chars) : chars);
65 (*pbegin) += chars;
66 free(buf);
67 return chars;
68 }
69 return vsnprintf(NULL, 0, format, ap);
70#else
71 va_list aq;
72 va_copy(aq, ap);
73 int chars = vsnprintf(NULL, 0, format, aq);
74 va_end(aq);
75 assert(chars > 0);
76 if (pbegin && *pbegin && (!end || *pbegin < end)) {
77 char buf[chars + 1];
78 vsprintf(buf, format, ap);
79 memcpy(*pbegin, buf, end ? MIN(end - *pbegin, chars) : chars);
80 (*pbegin) += chars;
81 }
82 return chars;
83#endif
84}
85
86size_t
87print_utf8(char **pbegin, char *end, char32_t c32)
88{
89 static const unsigned char mark[] = { 0x00, 0xc0, 0xe0, 0xf0 };
90
91 // Fast path for ASCII characters.
92 if (c32 <= 0x7f)
93 return print_char(pbegin, end, c32);
94
95 // Replace invalid characters by the replacement character (U+FFFD).
96 if ((c32 >= 0xd800 && c32 <= 0xdfff) || c32 > 0x10ffff)
97 c32 = 0xfffd;
98
99 int n = c32 <= 0x07ff ? 1 : (c32 <= 0xffff ? 2 : 3);
100 size_t chars = print_char(
101 pbegin, end, ((c32 >> (n * 6)) & 0x3f) | mark[n]);
102 while (n--)
103 chars += print_char(
104 pbegin, end, ((c32 >> (n * 6)) & 0x3f) | 0x80);
105 return chars;
106}
107
108size_t
109print_c99_esc(char **pbegin, char *end, char32_t c32)
110{
111 size_t chars = 0;
112
113 if (c32 < 0x80) {
114 switch (c32) {
115 case '\'':
116 chars += print_char(pbegin, end, '\\');
117 chars += print_char(pbegin, end, '\'');
118 break;
119 case '\"':
120 chars += print_char(pbegin, end, '\\');
121 chars += print_char(pbegin, end, '\"');
122 break;
123 case '\\':
124 chars += print_char(pbegin, end, '\\');
125 chars += print_char(pbegin, end, '\\');
126 break;
127 case '\a':
128 chars += print_char(pbegin, end, '\\');
129 chars += print_char(pbegin, end, 'a');
130 break;
131 case '\b':
132 chars += print_char(pbegin, end, '\\');
133 chars += print_char(pbegin, end, 'b');
134 break;
135 case '\f':
136 chars += print_char(pbegin, end, '\\');
137 chars += print_char(pbegin, end, 'f');
138 break;
139 case '\n':
140 chars += print_char(pbegin, end, '\\');
141 chars += print_char(pbegin, end, 'n');
142 break;
143 case '\r':
144 chars += print_char(pbegin, end, '\\');
145 chars += print_char(pbegin, end, 'r');
146 break;
147 case '\t':
148 chars += print_char(pbegin, end, '\\');
149 chars += print_char(pbegin, end, 't');
150 break;
151 case '\v':
152 chars += print_char(pbegin, end, '\\');
153 chars += print_char(pbegin, end, 'v');
154 break;
155 default:
156 if (isprint(c32)) {
157 chars += print_char(pbegin, end, c32);
158 } else {
159 // For non-printable characters, we use an octal
160 // escape sequence.
161 chars += print_char(pbegin, end, '\\');
162 if ((c32 >> 6) & 7)
163 chars += print_char(pbegin, end,
164 otoc(c32 >> 6));
165 if ((c32 >> 3) & 7)
166 chars += print_char(pbegin, end,
167 otoc(c32 >> 3));
168 chars += print_char(pbegin, end, otoc(c32));
169 }
170 break;
171 }
172 } else if ((c32 < 0xd800 || c32 > 0xdfff) && c32 <= 0x10ffff) {
173 chars += print_utf8(pbegin, end, c32);
174 } else {
175 // For invalid Unicode code points, we use a hexadecimal escape
176 // sequence.
177 chars += print_char(pbegin, end, '\\');
178 chars += print_char(pbegin, end, 'x');
179 // Compute the number of hex digits.
180 int n = 1;
181 while (c32 >> (4 * n))
182 n++;
183 // Print the hex digits.
184 for (int i = 0; i < n; i++)
185 chars += print_char(pbegin, end,
186 xtoc(c32 >> (4 * (n - i - 1))));
187 }
188
189 return chars;
190}
191
192size_t
193print_c99_str(char **pbegin, char *end, const char *s, size_t n)
194{
195 assert(s);
196
197 // cppcheck-suppress nullPointerArithmeticRedundantCheck
198 const char *ends = s + (s ? n : 0);
199
200 size_t chars = 0;
201 while (s < ends) {
202 char32_t c32 = 0;
203 s += lex_utf8(s, ends, NULL, &c32);
204 chars += print_c99_esc(pbegin, end, c32);
205 }
206 return chars;
207}
208
209#define LELY_UTIL_DEFINE_PRINT(type, suffix, name, format) \
210 size_t print_c99_##suffix(char **pbegin, char *end, type name) \
211 { \
212 return print_fmt(pbegin, end, format, name); \
213 }
214
215LELY_UTIL_DEFINE_PRINT(long, long, l, "%li")
216LELY_UTIL_DEFINE_PRINT(unsigned long, ulong, ul, "%lu")
217LELY_UTIL_DEFINE_PRINT(long long, llong, ll, "%" LENll "i")
218LELY_UTIL_DEFINE_PRINT(unsigned long long, ullong, ull, "%" LENll "u")
219
220#undef LELY_UTIL_DEFINE_PRINT
221
222#define LELY_UTIL_DEFINE_PRINT(type, suffix, name, format, dig) \
223 size_t print_c99_##suffix(char **pbegin, char *end, type name) \
224 { \
225 return print_fmt(pbegin, end, format, dig, name); \
226 }
227
228LELY_UTIL_DEFINE_PRINT(float, flt, f, "%.*g", FLT_DIG)
229LELY_UTIL_DEFINE_PRINT(double, dbl, d, "%.*g", DBL_DIG)
230#ifndef _WIN32
231LELY_UTIL_DEFINE_PRINT(long double, ldbl, ld, "%.*Lg", LDBL_DIG)
232#endif
233
234#undef LELY_UTIL_DEFINE_PRINT
235
236#define LELY_UTIL_DEFINE_PRINT(type, suffix, name, alias) \
237 size_t print_c99_##suffix(char **pbegin, char *end, type name) \
238 { \
239 return print_c99_##alias(pbegin, end, name); \
240 }
241
242LELY_UTIL_DEFINE_PRINT(int_least8_t, i8, i8, long)
243LELY_UTIL_DEFINE_PRINT(int_least16_t, i16, i16, long)
244LELY_UTIL_DEFINE_PRINT(int_least32_t, i32, i32, long)
245#if LONG_BIT == 32
246LELY_UTIL_DEFINE_PRINT(int_least64_t, i64, i64, llong)
247#else
248LELY_UTIL_DEFINE_PRINT(int_least64_t, i64, i64, long)
249#endif
250LELY_UTIL_DEFINE_PRINT(uint_least8_t, u8, u8, ulong)
251LELY_UTIL_DEFINE_PRINT(uint_least16_t, u16, u16, ulong)
252LELY_UTIL_DEFINE_PRINT(uint_least32_t, u32, u32, ulong)
253#if LONG_BIT == 32
254LELY_UTIL_DEFINE_PRINT(uint_least64_t, u64, u64, ullong)
255#else
256LELY_UTIL_DEFINE_PRINT(uint_least64_t, u64, u64, ulong)
257#endif
258
259#undef LELY_UTIL_DEFINE_PRINT
260
261size_t
262print_base64(char **pbegin, char *end, const void *ptr, size_t n)
263{
264 // clang-format off
265 static const char tab[64] =
266 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
267 "ghijklmnopqrstuvwxyz0123456789+/";
268 // clang-format on
269
270 size_t chars = 0;
271
272 const unsigned char *bp = ptr;
273 while (n) {
274 char c = tab[(bp[0] >> 2) & 0x3f];
275 chars += print_char(pbegin, end, c);
276 if (!((chars + 2) % 78)) {
277 chars += print_char(pbegin, end, '\r');
278 chars += print_char(pbegin, end, '\n');
279 }
280
281 c = tab[((bp[0] << 4) + (--n ? (bp[1] >> 4) : 0)) & 0x3f];
282 chars += print_char(pbegin, end, c);
283 if (!((chars + 2) % 78)) {
284 chars += print_char(pbegin, end, '\r');
285 chars += print_char(pbegin, end, '\n');
286 }
287
288 // clang-format off
289 c = n ? tab[((bp[1] << 2) + (--n ? (bp[2] >> 6) : 0)) & 0x3f]
290 : '=';
291 // clang-format on
292 chars += print_char(pbegin, end, c);
293 if (!((chars + 2) % 78)) {
294 chars += print_char(pbegin, end, '\r');
295 chars += print_char(pbegin, end, '\n');
296 }
297
298 c = n ? (--n, tab[bp[2] & 0x3f]) : '=';
299 chars += print_char(pbegin, end, c);
300 if (n && !((chars + 2) % 78)) {
301 chars += print_char(pbegin, end, '\r');
302 chars += print_char(pbegin, end, '\n');
303 }
304
305 bp += 3;
306 }
307
308 return chars;
309}
310
311#endif // !LELY_NO_STDIO
This header file is part of the utilities library; it contains the IEEE 754 floating-point format typ...
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
This header file is part of the utilities library; it contains the lexer function declarations.
size_t lex_utf8(const char *begin, const char *end, struct floc *at, char32_t *pc32)
Lexes a UTF-8 encoded Unicode character from a memory buffer.
Definition: lex.c:86
size_t print_c99_str(char **pbegin, char *end, const char *s, size_t n)
Prints a UTF-8 encoded Unicode string to a memory buffer.
Definition: print.c:193
size_t vprint_fmt(char **pbegin, char *end, const char *format, va_list ap)
Prints a formatted string to a memory buffer.
Definition: print.c:53
size_t print_c99_esc(char **pbegin, char *end, char32_t c32)
Prints a UTF-8 encoded Unicode character to a memory buffer.
Definition: print.c:109
size_t print_base64(char **pbegin, char *end, const void *ptr, size_t n)
Prints the Base64 representation of binary data to a memory buffer.
Definition: print.c:262
size_t print_utf8(char **pbegin, char *end, char32_t c32)
Prints a UTF-8 encoded Unicode character to a memory buffer.
Definition: print.c:87
size_t print_fmt(char **pbegin, char *end, const char *format,...)
Prints a formatted string to a memory buffer.
Definition: print.c:43
This header file is part of the utilities library; it contains the printing function declarations.
#define LENll
A cross-platform "ll" length modifier macro for format specifiers.
Definition: print.h:45
size_t print_char(char **pbegin, char *end, int c)
Prints a single character to a memory buffer.
Definition: print.h:286
int xtoc(int i)
Returns the character corresponding to the hexadecimal digit i.
Definition: print.h:265
int otoc(int i)
Returns the character corresponding to the octal digit i.
Definition: print.h:259
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint....
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
int vasprintf(char **strp, const char *fmt, va_list ap)
Equivalent to vsprintf(), except that it allocates a string large enough to hold the output,...
Definition: stdio.c:116
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
This header file is part of the C11 and POSIX compatibility library; it includes <uchar....