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 
42 size_t
43 print_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 
52 size_t
53 vprint_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 
86 size_t
87 print_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 
108 size_t
109 print_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 
192 size_t
193 print_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 
215 LELY_UTIL_DEFINE_PRINT(long, long, l, "%li")
216 LELY_UTIL_DEFINE_PRINT(unsigned long, ulong, ul, "%lu")
217 LELY_UTIL_DEFINE_PRINT(long long, llong, ll, "%" LENll "i")
218 LELY_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 
228 LELY_UTIL_DEFINE_PRINT(float, flt, f, "%.*g", FLT_DIG)
229 LELY_UTIL_DEFINE_PRINT(double, dbl, d, "%.*g", DBL_DIG)
230 #ifndef _WIN32
231 LELY_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 
242 LELY_UTIL_DEFINE_PRINT(int_least8_t, i8, i8, long)
243 LELY_UTIL_DEFINE_PRINT(int_least16_t, i16, i16, long)
244 LELY_UTIL_DEFINE_PRINT(int_least32_t, i32, i32, long)
245 #if LONG_BIT == 32
246 LELY_UTIL_DEFINE_PRINT(int_least64_t, i64, i64, llong)
247 #else
248 LELY_UTIL_DEFINE_PRINT(int_least64_t, i64, i64, long)
249 #endif
250 LELY_UTIL_DEFINE_PRINT(uint_least8_t, u8, u8, ulong)
251 LELY_UTIL_DEFINE_PRINT(uint_least16_t, u16, u16, ulong)
252 LELY_UTIL_DEFINE_PRINT(uint_least32_t, u32, u32, ulong)
253 #if LONG_BIT == 32
254 LELY_UTIL_DEFINE_PRINT(uint_least64_t, u64, u64, ullong)
255 #else
256 LELY_UTIL_DEFINE_PRINT(uint_least64_t, u64, u64, ulong)
257 #endif
258 
259 #undef LELY_UTIL_DEFINE_PRINT
260 
261 size_t
262 print_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....