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