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