• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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