Lely core libraries  2.2.5
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 
39 size_t
40 print_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 
49 size_t
50 vprint_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 
83 size_t
84 print_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 
105 size_t
106 print_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 
189 size_t
190 print_c99_str(char **pbegin, char *end, const char *s, size_t n)
191 {
192  assert(s);
193 
194  // cppcheck-suppress nullPointerArithmeticRedundantCheck
195  const char *ends = s + (s ? n : 0);
196 
197  size_t chars = 0;
198  while (s < ends) {
199  char32_t c32 = 0;
200  s += lex_utf8(s, ends, NULL, &c32);
201  chars += print_c99_esc(pbegin, end, c32);
202  }
203  return chars;
204 }
205 
206 #define LELY_UTIL_DEFINE_PRINT(type, suffix, name, format) \
207  size_t print_c99_##suffix(char **pbegin, char *end, type name) \
208  { \
209  return print_fmt(pbegin, end, format, name); \
210  }
211 
212 LELY_UTIL_DEFINE_PRINT(long, long, l, "%li")
213 LELY_UTIL_DEFINE_PRINT(unsigned long, ulong, ul, "%lu")
214 LELY_UTIL_DEFINE_PRINT(long long, llong, ll, "%" LENll "i")
215 LELY_UTIL_DEFINE_PRINT(unsigned long long, ullong, ull, "%" LENll "u")
216 
217 #undef LELY_UTIL_DEFINE_PRINT
218 
219 #define LELY_UTIL_DEFINE_PRINT(type, suffix, name, format, dig) \
220  size_t print_c99_##suffix(char **pbegin, char *end, type name) \
221  { \
222  return print_fmt(pbegin, end, format, dig, name); \
223  }
224 
225 LELY_UTIL_DEFINE_PRINT(float, flt, f, "%.*g", FLT_DIG)
226 LELY_UTIL_DEFINE_PRINT(double, dbl, d, "%.*g", DBL_DIG)
227 #ifndef _WIN32
228 LELY_UTIL_DEFINE_PRINT(long double, ldbl, ld, "%.*Lg", LDBL_DIG)
229 #endif
230 
231 #undef LELY_UTIL_DEFINE_PRINT
232 
233 #define LELY_UTIL_DEFINE_PRINT(type, suffix, name, alias) \
234  size_t print_c99_##suffix(char **pbegin, char *end, type name) \
235  { \
236  return print_c99_##alias(pbegin, end, name); \
237  }
238 
239 LELY_UTIL_DEFINE_PRINT(int_least8_t, i8, i8, long)
240 LELY_UTIL_DEFINE_PRINT(int_least16_t, i16, i16, long)
241 LELY_UTIL_DEFINE_PRINT(int_least32_t, i32, i32, long)
242 #if LONG_BIT == 32
243 LELY_UTIL_DEFINE_PRINT(int_least64_t, i64, i64, llong)
244 #else
245 LELY_UTIL_DEFINE_PRINT(int_least64_t, i64, i64, long)
246 #endif
247 LELY_UTIL_DEFINE_PRINT(uint_least8_t, u8, u8, ulong)
248 LELY_UTIL_DEFINE_PRINT(uint_least16_t, u16, u16, ulong)
249 LELY_UTIL_DEFINE_PRINT(uint_least32_t, u32, u32, ulong)
250 #if LONG_BIT == 32
251 LELY_UTIL_DEFINE_PRINT(uint_least64_t, u64, u64, ullong)
252 #else
253 LELY_UTIL_DEFINE_PRINT(uint_least64_t, u64, u64, ulong)
254 #endif
255 
256 #undef LELY_UTIL_DEFINE_PRINT
257 
258 size_t
259 print_base64(char **pbegin, char *end, const void *ptr, size_t n)
260 {
261  // clang-format off
262  static const char tab[64] =
263  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
264  "ghijklmnopqrstuvwxyz0123456789+/";
265  // clang-format on
266 
267  size_t chars = 0;
268 
269  const unsigned char *bp = ptr;
270  while (n) {
271  char c = tab[(bp[0] >> 2) & 0x3f];
272  chars += print_char(pbegin, end, c);
273  if (!((chars + 2) % 78)) {
274  chars += print_char(pbegin, end, '\r');
275  chars += print_char(pbegin, end, '\n');
276  }
277 
278  c = tab[((bp[0] << 4) + (--n ? (bp[1] >> 4) : 0)) & 0x3f];
279  chars += print_char(pbegin, end, c);
280  if (!((chars + 2) % 78)) {
281  chars += print_char(pbegin, end, '\r');
282  chars += print_char(pbegin, end, '\n');
283  }
284 
285  // clang-format off
286  c = n ? tab[((bp[1] << 2) + (--n ? (bp[2] >> 6) : 0)) & 0x3f]
287  : '=';
288  // clang-format on
289  chars += print_char(pbegin, end, c);
290  if (!((chars + 2) % 78)) {
291  chars += print_char(pbegin, end, '\r');
292  chars += print_char(pbegin, end, '\n');
293  }
294 
295  c = n ? (--n, tab[bp[2] & 0x3f]) : '=';
296  chars += print_char(pbegin, end, c);
297  if (n && !((chars + 2) % 78)) {
298  chars += print_char(pbegin, end, '\r');
299  chars += print_char(pbegin, end, '\n');
300  }
301 
302  bp += 3;
303  }
304 
305  return chars;
306 }
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 <string.h> and defines any missing functionality.
#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 IEEE 754 floating-point format typ...
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
This header file is part of the utilities library; it contains the lexer function declarations...
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <uchar.h>, if it exists, and defines any missing functionality.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint.h> and defines any missing functionality.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio.h> and defines any missing functionality.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.