1 #include <stdarg.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #ifdef _WIN32
6 #include <windows.h>
7 #else
8 #include <unistd.h>
9 #endif
10 #ifdef __ANDROID__
11 #include <android/log.h>
12 #endif
13 #ifdef __hexagon__
14 #include <qurt_printf.h>
15 #endif
16
17 #ifndef CLOG_LOG_TO_STDIO
18 #ifdef __ANDROID__
19 #define CLOG_LOG_TO_STDIO 0
20 #else
21 #define CLOG_LOG_TO_STDIO 1
22 #endif
23 #endif
24
25 #include <clog.h>
26
27
28 /* Messages up to this size are formatted entirely on-stack, and don't allocate heap memory */
29 #define CLOG_STACK_BUFFER_SIZE 1024
30
31 #define CLOG_FATAL_PREFIX "Fatal error: "
32 #define CLOG_FATAL_PREFIX_LENGTH 13
33 #define CLOG_FATAL_PREFIX_FORMAT "Fatal error in %s: "
34 #define CLOG_ERROR_PREFIX "Error: "
35 #define CLOG_ERROR_PREFIX_LENGTH 7
36 #define CLOG_ERROR_PREFIX_FORMAT "Error in %s: "
37 #define CLOG_WARNING_PREFIX "Warning: "
38 #define CLOG_WARNING_PREFIX_LENGTH 9
39 #define CLOG_WARNING_PREFIX_FORMAT "Warning in %s: "
40 #define CLOG_INFO_PREFIX "Note: "
41 #define CLOG_INFO_PREFIX_LENGTH 6
42 #define CLOG_INFO_PREFIX_FORMAT "Note (%s): "
43 #define CLOG_DEBUG_PREFIX "Debug: "
44 #define CLOG_DEBUG_PREFIX_LENGTH 7
45 #define CLOG_DEBUG_PREFIX_FORMAT "Debug (%s): "
46 #define CLOG_SUFFIX_LENGTH 1
47
clog_vlog_fatal(const char * module,const char * format,va_list args)48 void clog_vlog_fatal(const char* module, const char* format, va_list args) {
49 #if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
50 __android_log_vprint(ANDROID_LOG_FATAL, module, format, args);
51 #else
52 char stack_buffer[CLOG_STACK_BUFFER_SIZE];
53 char* heap_buffer = NULL;
54 char* out_buffer = &stack_buffer[0];
55
56 /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
57 va_list args_copy;
58 va_copy(args_copy, args);
59
60 int prefix_chars = CLOG_FATAL_PREFIX_LENGTH;
61 if (module == NULL) {
62 memcpy(stack_buffer, CLOG_FATAL_PREFIX, CLOG_FATAL_PREFIX_LENGTH);
63 } else {
64 prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_FATAL_PREFIX_FORMAT, module);
65 if (prefix_chars < 0) {
66 /* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
67 prefix_chars = 0;
68 }
69 }
70
71 int format_chars;
72 if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
73 /*
74 * Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
75 * Do not even try to format the string into on-stack buffer.
76 */
77 format_chars = vsnprintf(NULL, 0, format, args);
78 } else {
79 format_chars =
80 vsnprintf(
81 &stack_buffer[prefix_chars],
82 CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
83 format,
84 args);
85 }
86 if (format_chars < 0) {
87 /* Format error in the message: silently ignore this particular message. */
88 goto cleanup;
89 }
90 if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
91 /* Allocate a buffer on heap, and vsnprintf to this buffer */
92 heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
93 if (heap_buffer == NULL) {
94 goto cleanup;
95 }
96
97 if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
98 /* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
99 snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_FATAL_PREFIX_FORMAT, module);
100 } else {
101 /* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
102 memcpy(heap_buffer, stack_buffer, prefix_chars);
103 }
104 vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
105 out_buffer = heap_buffer;
106 }
107 out_buffer[prefix_chars + format_chars] = '\n';
108 #if defined(_WIN32)
109 DWORD bytes_written;
110 WriteFile(
111 GetStdHandle(STD_ERROR_HANDLE),
112 out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
113 &bytes_written, NULL);
114 #elif defined(__hexagon__)
115 qurt_printf("%s", out_buffer);
116 #else
117 write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
118 #endif
119
120 cleanup:
121 free(heap_buffer);
122 va_end(args_copy);
123 #endif
124 }
125
clog_vlog_error(const char * module,const char * format,va_list args)126 void clog_vlog_error(const char* module, const char* format, va_list args) {
127 #if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
128 __android_log_vprint(ANDROID_LOG_ERROR, module, format, args);
129 #else
130 char stack_buffer[CLOG_STACK_BUFFER_SIZE];
131 char* heap_buffer = NULL;
132 char* out_buffer = &stack_buffer[0];
133
134 /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
135 va_list args_copy;
136 va_copy(args_copy, args);
137
138 int prefix_chars = CLOG_ERROR_PREFIX_LENGTH;
139 if (module == NULL) {
140 memcpy(stack_buffer, CLOG_ERROR_PREFIX, CLOG_ERROR_PREFIX_LENGTH);
141 } else {
142 prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_ERROR_PREFIX_FORMAT, module);
143 if (prefix_chars < 0) {
144 /* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
145 prefix_chars = 0;
146 }
147 }
148
149 int format_chars;
150 if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
151 /*
152 * Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
153 * Do not even try to format the string into on-stack buffer.
154 */
155 format_chars = vsnprintf(NULL, 0, format, args);
156 } else {
157 format_chars =
158 vsnprintf(
159 &stack_buffer[prefix_chars],
160 CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
161 format,
162 args);
163 }
164 if (format_chars < 0) {
165 /* Format error in the message: silently ignore this particular message. */
166 goto cleanup;
167 }
168 if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
169 /* Allocate a buffer on heap, and vsnprintf to this buffer */
170 heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
171 if (heap_buffer == NULL) {
172 goto cleanup;
173 }
174
175 if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
176 /* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
177 snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_ERROR_PREFIX_FORMAT, module);
178 } else {
179 /* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
180 memcpy(heap_buffer, stack_buffer, prefix_chars);
181 }
182 vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
183 out_buffer = heap_buffer;
184 }
185 out_buffer[prefix_chars + format_chars] = '\n';
186 #if defined(_WIN32)
187 DWORD bytes_written;
188 WriteFile(
189 GetStdHandle(STD_ERROR_HANDLE),
190 out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
191 &bytes_written, NULL);
192 #elif defined(__hexagon__)
193 qurt_printf("%s", out_buffer);
194 #else
195 write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
196 #endif
197
198 cleanup:
199 free(heap_buffer);
200 va_end(args_copy);
201 #endif
202 }
203
clog_vlog_warning(const char * module,const char * format,va_list args)204 void clog_vlog_warning(const char* module, const char* format, va_list args) {
205 #if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
206 __android_log_vprint(ANDROID_LOG_WARN, module, format, args);
207 #else
208 char stack_buffer[CLOG_STACK_BUFFER_SIZE];
209 char* heap_buffer = NULL;
210 char* out_buffer = &stack_buffer[0];
211
212 /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
213 va_list args_copy;
214 va_copy(args_copy, args);
215
216 int prefix_chars = CLOG_WARNING_PREFIX_LENGTH;
217 if (module == NULL) {
218 memcpy(stack_buffer, CLOG_WARNING_PREFIX, CLOG_WARNING_PREFIX_LENGTH);
219 } else {
220 prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_WARNING_PREFIX_FORMAT, module);
221 if (prefix_chars < 0) {
222 /* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
223 prefix_chars = 0;
224 }
225 }
226
227 int format_chars;
228 if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
229 /*
230 * Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
231 * Do not even try to format the string into on-stack buffer.
232 */
233 format_chars = vsnprintf(NULL, 0, format, args);
234 } else {
235 format_chars =
236 vsnprintf(
237 &stack_buffer[prefix_chars],
238 CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
239 format,
240 args);
241 }
242 if (format_chars < 0) {
243 /* Format error in the message: silently ignore this particular message. */
244 goto cleanup;
245 }
246 if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
247 /* Allocate a buffer on heap, and vsnprintf to this buffer */
248 heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
249 if (heap_buffer == NULL) {
250 goto cleanup;
251 }
252
253 if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
254 /* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
255 snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_WARNING_PREFIX_FORMAT, module);
256 } else {
257 /* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
258 memcpy(heap_buffer, stack_buffer, prefix_chars);
259 }
260 vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
261 out_buffer = heap_buffer;
262 }
263 out_buffer[prefix_chars + format_chars] = '\n';
264 #if defined(_WIN32)
265 DWORD bytes_written;
266 WriteFile(
267 GetStdHandle(STD_ERROR_HANDLE),
268 out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
269 &bytes_written, NULL);
270 #elif defined(__hexagon__)
271 qurt_printf("%s", out_buffer);
272 #else
273 write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
274 #endif
275
276 cleanup:
277 free(heap_buffer);
278 va_end(args_copy);
279 #endif
280 }
281
clog_vlog_info(const char * module,const char * format,va_list args)282 void clog_vlog_info(const char* module, const char* format, va_list args) {
283 #if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
284 __android_log_vprint(ANDROID_LOG_INFO, module, format, args);
285 #else
286 char stack_buffer[CLOG_STACK_BUFFER_SIZE];
287 char* heap_buffer = NULL;
288 char* out_buffer = &stack_buffer[0];
289
290 /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
291 va_list args_copy;
292 va_copy(args_copy, args);
293
294 int prefix_chars = CLOG_INFO_PREFIX_LENGTH;
295 if (module == NULL) {
296 memcpy(stack_buffer, CLOG_INFO_PREFIX, CLOG_INFO_PREFIX_LENGTH);
297 } else {
298 prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_INFO_PREFIX_FORMAT, module);
299 if (prefix_chars < 0) {
300 /* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
301 prefix_chars = 0;
302 }
303 }
304
305 int format_chars;
306 if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
307 /*
308 * Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
309 * Do not even try to format the string into on-stack buffer.
310 */
311 format_chars = vsnprintf(NULL, 0, format, args);
312 } else {
313 format_chars =
314 vsnprintf(
315 &stack_buffer[prefix_chars],
316 CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
317 format,
318 args);
319 }
320 if (format_chars < 0) {
321 /* Format error in the message: silently ignore this particular message. */
322 goto cleanup;
323 }
324 if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
325 /* Allocate a buffer on heap, and vsnprintf to this buffer */
326 heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
327 if (heap_buffer == NULL) {
328 goto cleanup;
329 }
330
331 if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
332 /* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
333 snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_INFO_PREFIX_FORMAT, module);
334 } else {
335 /* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
336 memcpy(heap_buffer, stack_buffer, prefix_chars);
337 }
338 vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
339 out_buffer = heap_buffer;
340 }
341 out_buffer[prefix_chars + format_chars] = '\n';
342 #if defined(_WIN32)
343 DWORD bytes_written;
344 WriteFile(
345 GetStdHandle(STD_OUTPUT_HANDLE),
346 out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
347 &bytes_written, NULL);
348 #elif defined(__hexagon__)
349 qurt_printf("%s", out_buffer);
350 #else
351 write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
352 #endif
353
354 cleanup:
355 free(heap_buffer);
356 va_end(args_copy);
357 #endif
358 }
359
clog_vlog_debug(const char * module,const char * format,va_list args)360 void clog_vlog_debug(const char* module, const char* format, va_list args) {
361 #if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
362 __android_log_vprint(ANDROID_LOG_DEBUG, module, format, args);
363 #else
364 char stack_buffer[CLOG_STACK_BUFFER_SIZE];
365 char* heap_buffer = NULL;
366 char* out_buffer = &stack_buffer[0];
367
368 /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
369 va_list args_copy;
370 va_copy(args_copy, args);
371
372 int prefix_chars = CLOG_DEBUG_PREFIX_LENGTH;
373 if (module == NULL) {
374 memcpy(stack_buffer, CLOG_DEBUG_PREFIX, CLOG_DEBUG_PREFIX_LENGTH);
375 } else {
376 prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_DEBUG_PREFIX_FORMAT, module);
377 if (prefix_chars < 0) {
378 /* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
379 prefix_chars = 0;
380 }
381 }
382
383 int format_chars;
384 if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
385 /*
386 * Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
387 * Do not even try to format the string into on-stack buffer.
388 */
389 format_chars = vsnprintf(NULL, 0, format, args);
390 } else {
391 format_chars =
392 vsnprintf(
393 &stack_buffer[prefix_chars],
394 CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
395 format,
396 args);
397 }
398 if (format_chars < 0) {
399 /* Format error in the message: silently ignore this particular message. */
400 goto cleanup;
401 }
402 if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
403 /* Allocate a buffer on heap, and vsnprintf to this buffer */
404 heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
405 if (heap_buffer == NULL) {
406 goto cleanup;
407 }
408
409 if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
410 /* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
411 snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_DEBUG_PREFIX_FORMAT, module);
412 } else {
413 /* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
414 memcpy(heap_buffer, stack_buffer, prefix_chars);
415 }
416 vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
417 out_buffer = heap_buffer;
418 }
419 out_buffer[prefix_chars + format_chars] = '\n';
420 #if defined(_WIN32)
421 DWORD bytes_written;
422 WriteFile(
423 GetStdHandle(STD_OUTPUT_HANDLE),
424 out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
425 &bytes_written, NULL);
426 #elif defined(__hexagon__)
427 qurt_printf("%s", out_buffer);
428 #else
429 write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
430 #endif
431
432 cleanup:
433 free(heap_buffer);
434 va_end(args_copy);
435 #endif
436 }
437