1 /* $Id: log.c,v 1.3 2009/08/30 17:04:51 fredette Exp $ */
2
3 /* libtme/log.c - logging functions: */
4
5 /*
6 * Copyright (c) 2003 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <tme/common.h>
37 _TME_RCSID("$Id: log.c,v 1.3 2009/08/30 17:04:51 fredette Exp $");
38
39 /* includes: */
40 #include <tme/log.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <errno.h>
45 #ifdef HAVE_STDARG_H
46 #include <stdarg.h>
47 #else /* HAVE_STDARG_H */
48 #include <varargs.h>
49 #endif /* HAVE_STDARG_H */
50 #include <tme/hash.h>
51
52 /* argument codes: */
53 #define TME_LOG_ARG_CODE_NULL (0)
54 #define TME_LOG_ARG_CODE_STRING (1)
55 #define TME_LOG_ARG_CODE_CHAR (2)
56 #define TME_LOG_ARG_CODE_INT (3)
57 #define TME_LOG_ARG_CODE_LONG_INT (4)
58 #define TME_LOG_ARG_CODE_LONG_LONG_INT (5)
59
60 /* if long long int is supported, this macro expands its argument,
61 otherwise it doesn't: */
62 #if defined(__GNUC__) && (__GNUC__ >= 2) && (_TME_SIZEOF_INT == 4)
63 #define prf_lld(x) x
64 #else /* defined(__GNUC__) && (__GNUC__ >= 2) && (_TME_SIZEOF_INT == 4) */
65 #define prf_lld(x) /**/
66 #endif /* defined(__GNUC__) && (__GNUC__ >= 2) && (_TME_SIZEOF_INT == 4) */
67
68 /* if tme_int64_t is supported, this macro expands its argument,
69 otherwise it doesn't: */
70 #ifdef TME_HAVE_INT64_T
71 #define _TME_LOG_IF_INT64_T(x) x
72 #else /* !TME_HAVE_INT64_T */
73 #define _TME_LOG_IF_INT64_T(x) /**/
74 #endif /* !TME_HAVE_INT64_T */
75
76 /* this returns nonzero if the type of the given expression can have
77 any alignment: */
78 #define _TME_LOG_ALIGN_ANY(x) \
79 (sizeof(x) <= sizeof(tme_int32_t) \
80 ? _TME_ALIGNOF_INT32_T == 1 \
81 _TME_LOG_IF_INT64_T(: sizeof(x) == sizeof(tme_int64_t) \
82 ? _TME_ALIGNOF_INT64_T == 1) \
83 : FALSE)
84
85 /* the locals needed for log-prf.c: */
86 #define LOG_PRF_LOCALS \
87 va_list prf_args; \
88 int prf_state; \
89 const char *prf_agg; \
90 char prf_char; \
91 int prf_width; \
92 const char *prf_flag_ls; \
93 int prf_flag_0; \
94 int prf_digit; \
95 int prf_value_d; \
96 long int prf_value_ld; \
97 prf_lld(long long int prf_value_lld;) \
98 const char *prf_value_s; \
99 char prf_value_c; \
100 char *prf_value_buffer; \
101 /* the largest prf_format_buffer needed is: \
102 % 0 INT ll CONVERSION NUL: */ \
103 char prf_format_buffer[1 + 1 + (sizeof(int) * 3) + 2 + 1 + 1]
104
105 /* the argument code type: */
106 typedef tme_uint8_t tme_log_arg_code_t;
107
108 /* this appends raw output: */
109 void
tme_output_append_raw(char ** _output,const char * output_new,unsigned int output_new_length)110 tme_output_append_raw(char **_output, const char *output_new, unsigned int output_new_length)
111 {
112 char *output;
113 unsigned int output_length;
114 int saved_errno;
115
116 saved_errno = errno;
117 output = *_output;
118 if (output == NULL) {
119 output_length = 0;
120 output = tme_new(char, output_new_length + 1);
121 }
122 else {
123 output_length = strlen(output);
124 output = tme_renew(char, output, output_length + output_new_length + 1);
125 }
126 memcpy(output + output_length, output_new, output_new_length);
127 output[output_length + output_new_length] = '\0';
128 *_output = output;
129 errno = saved_errno;
130 }
131
132 /* this prepends raw output: */
133 void
tme_output_prepend_raw(char ** _output,const char * output_new,unsigned int output_new_length)134 tme_output_prepend_raw(char **_output, const char *output_new, unsigned int output_new_length)
135 {
136 char *output;
137 unsigned int output_length;
138 int saved_errno;
139
140 saved_errno = errno;
141 output = *_output;
142 if (output == NULL) {
143 output_length = 0;
144 output = tme_new(char, output_new_length + 1);
145 }
146 else {
147 output_length = strlen(output);
148 output = tme_renew(char, output, output_length + output_new_length + 1);
149 }
150 memmove(output + output_new_length, output, output_length);
151 memcpy(output, output_new, output_new_length);
152 output[output_length + output_new_length] = '\0';
153 *_output = output;
154 errno = saved_errno;
155 }
156
157 /* this appends or prepends a character: */
158 static void
tme_output_xpend_char(char ** _output,char output_char,int prepend)159 tme_output_xpend_char(char **_output, char output_char, int prepend)
160 {
161 char *output;
162 unsigned int output_length;
163 int saved_errno;
164
165 saved_errno = errno;
166 output = *_output;
167 if (output == NULL) {
168 output_length = 0;
169 output = tme_new(char, 2);
170 }
171 else {
172 output_length = strlen(output);
173 output = tme_renew(char, output, output_length + 2);
174 }
175 if (prepend) {
176 memmove(output + 1, output, output_length);
177 output[0] = output_char;
178 }
179 else {
180 output[output_length] = output_char;
181 }
182 output[output_length + 1] = '\0';
183 *_output = output;
184 errno = saved_errno;
185 }
186
187 /* this appends printf-style output: */
188 #ifdef HAVE_STDARG_H
tme_output_append(char ** _output,const char * prf_format,...)189 void tme_output_append(char **_output, const char *prf_format, ...)
190 #else /* HAVE_STDARG_H */
191 void tme_output_append(_output, prf_format, va_alist)
192 char **_output;
193 const char *prf_format;
194 va_dcl
195 #endif /* HAVE_STDARG_H */
196 {
197 LOG_PRF_LOCALS;
198
199 #define PRF_OUT_ARG_CODE(arg_code) do { } while (/* CONSTCOND */ 0 && (arg_code))
200 #define PRF_OUT_MEM(s, len) tme_output_append_raw(_output, s, len)
201 #define PRF_OUT_CHAR(c) tme_output_xpend_char(_output, c, FALSE)
202
203 do
204 #include "log-prf.c"
205 while (/* CONSTCOND */ 0);
206
207 #undef PRF_OUT_ARG_CODE
208 #undef PRF_OUT_MEM
209 #undef PRF_OUT_CHAR
210 }
211
212 /* this appends printf-style output for an error: */
213 #ifdef HAVE_STDARG_H
tme_output_append_error(char ** _output,const char * prf_format,...)214 void tme_output_append_error(char **_output, const char *prf_format, ...)
215 #else /* HAVE_STDARG_H */
216 void tme_output_append_error(_output, prf_format, va_alist)
217 char **_output;
218 const char *prf_format;
219 va_dcl
220 #endif /* HAVE_STDARG_H */
221 {
222 LOG_PRF_LOCALS;
223
224 #define PRF_OUT_ARG_CODE(arg_code) do { } while (/* CONSTCOND */ 0 && (arg_code))
225 #define PRF_OUT_MEM(s, len) tme_output_append_raw(_output, s, len)
226 #define PRF_OUT_CHAR(c) tme_output_xpend_char(_output, c, FALSE)
227
228 do
229 #include "log-prf.c"
230 while (/* CONSTCOND */ 0);
231
232 #undef PRF_OUT_ARG_CODE
233 #undef PRF_OUT_MEM
234 #undef PRF_OUT_CHAR
235 }
236
237 /* this prepends printf-style output: */
238 #ifdef HAVE_STDARG_H
tme_output_prepend(char ** _output,const char * prf_format,...)239 void tme_output_prepend(char **_output, const char *prf_format, ...)
240 #else /* HAVE_STDARG_H */
241 void tme_output_prepend(_output, prf_format, va_alist)
242 char **_output;
243 const char *prf_format;
244 va_dcl
245 #endif /* HAVE_STDARG_H */
246 {
247 LOG_PRF_LOCALS;
248
249 #define PRF_OUT_ARG_CODE(arg_code) do { } while (/* CONSTCOND */ 0 && (arg_code))
250 #define PRF_OUT_MEM(s, len) tme_output_prepend_raw(_output, s, len)
251 #define PRF_OUT_CHAR(c) tme_output_xpend_char(_output, c, TRUE)
252
253 do
254 #include "log-prf.c"
255 while (/* CONSTCOND */ 0);
256
257 #undef PRF_OUT_ARG_CODE
258 #undef PRF_OUT_MEM
259 #undef PRF_OUT_CHAR
260 }
261
262 /* this prepends printf-style output for an error: */
263 #ifdef HAVE_STDARG_H
tme_output_prepend_error(char ** _output,const char * prf_format,...)264 void tme_output_prepend_error(char **_output, const char *prf_format, ...)
265 #else /* HAVE_STDARG_H */
266 void tme_output_prepend_error(_output, prf_format, va_alist)
267 char **_output;
268 const char *prf_format;
269 va_dcl
270 #endif /* HAVE_STDARG_H */
271 {
272 LOG_PRF_LOCALS;
273
274 #define PRF_OUT_ARG_CODE(arg_code) do { } while (/* CONSTCOND */ 0 && (arg_code))
275 #define PRF_OUT_MEM(s, len) tme_output_prepend_raw(_output, s, len)
276 #define PRF_OUT_CHAR(c) tme_output_xpend_char(_output, c, TRUE)
277
278 do
279 #include "log-prf.c"
280 while (/* CONSTCOND */ 0);
281
282 #undef PRF_OUT_ARG_CODE
283 #undef PRF_OUT_MEM
284 #undef PRF_OUT_CHAR
285 }
286
287 /* this saves an argument code: */
288 static void
_tme_log_arg_code(tme_log_arg_code_t ** _arg_codes,tme_log_arg_code_t arg_code)289 _tme_log_arg_code(tme_log_arg_code_t **_arg_codes,
290 tme_log_arg_code_t arg_code)
291 {
292 tme_log_arg_code_t *arg_codes;
293 unsigned long arg_code_count;
294
295 /* count the existing argument codes: */
296 arg_codes = *_arg_codes;
297 for (arg_code_count = 0;
298 arg_codes[arg_code_count] != TME_LOG_ARG_CODE_NULL;
299 arg_code_count++);
300
301 /* resize the argument codes: */
302 arg_codes = tme_renew(tme_log_arg_code_t, arg_codes, arg_code_count + 1);
303 *_arg_codes = arg_codes;
304
305 /* add in this new argument code: */
306 arg_codes[arg_code_count] = arg_code;
307 arg_codes[arg_code_count + 1] = TME_LOG_ARG_CODE_NULL;
308 }
309
310 /* this logs printf-style output: */
311 #ifdef HAVE_STDARG_H
tme_log_part(struct tme_log_handle * handle,const char * prf_format,...)312 void tme_log_part(struct tme_log_handle *handle, const char *prf_format, ...)
313 #else /* HAVE_STDARG_H */
314 void tme_log_part(handle, prf_format, va_alist)
315 struct tme_log_handle *handle;
316 const char *prf_format;
317 va_dcl
318 #endif /* HAVE_STDARG_H */
319 {
320 LOG_PRF_LOCALS;
321 char *message;
322 unsigned long message_size;
323 const char *prf_format_saved;
324 tme_log_arg_code_t *arg_codes;
325 tme_log_arg_code_t arg_code;
326 unsigned long string_length;
327 int saved_errno;
328
329 /* do nothing if we have no format: */
330 if (prf_format == NULL) {
331 return;
332 }
333
334 /* if this handle is in binary mode: */
335 if (handle->tme_log_handle_mode == TME_LOG_MODE_BINARY) {
336
337 /* get the current message: */
338 message = handle->tme_log_handle_message;
339 message_size = handle->tme_log_handle_message_size;
340
341 /* add the format to the message: */
342 prf_format_saved = prf_format;
343 assert ((message_size + sizeof(prf_format_saved)) <= TME_LOG_MESSAGE_SIZE_MAX_BINARY);
344 if (_TME_LOG_ALIGN_ANY(prf_format_saved)) {
345 *((const char **) (message + message_size)) = prf_format_saved;
346 }
347 else {
348 memcpy(message + message_size,
349 &prf_format_saved,
350 sizeof(prf_format_saved));
351 }
352 message_size += sizeof(prf_format_saved);
353
354 /* if this format isn't known: */
355 arg_codes = tme_hash_lookup(handle->tme_log_handle_hash_format, (void *) prf_format);
356 if (__tme_predict_false(arg_codes == NULL)) {
357
358 /* start the argument codes: */
359 arg_codes = tme_new(tme_log_arg_code_t, 1);
360 arg_codes[0] = TME_LOG_ARG_CODE_NULL;
361
362 /* get the argument codes for this format: */
363 #define PRF_OUT_ARG_CODE(arg_code) _tme_log_arg_code(&arg_codes, (arg_code))
364 #define PRF_OUT_MEM(s, len) do { } while (/* CONSTCOND */ 0 && (s) && (len))
365 #define PRF_OUT_CHAR(c) do { } while (/* CONSTCOND */ 0 && (c))
366
367 saved_errno = errno;
368 do
369 #include "log-prf.c"
370 while (/* CONSTCOND */ 0);
371 errno = saved_errno;
372
373 #undef PRF_OUT_ARG_CODE
374 #undef PRF_OUT_MEM
375 #undef PRF_OUT_CHAR
376
377 /* insert this format into the hash: */
378 tme_hash_insert(handle->tme_log_handle_hash_format, (void *) prf_format_saved, arg_codes);
379 }
380
381 /* start the variable arguments: */
382 #ifdef HAVE_STDARG_H
383 va_start(prf_args, prf_format);
384 #else /* HAVE_STDARG_H */
385 va_start(prf_args);
386 #endif /* HAVE_STDARG_H */
387
388 /* while we have variable arguments: */
389 for (; (arg_code = *(arg_codes++)) != TME_LOG_ARG_CODE_NULL; ) {
390
391 /* if this argument is a string: */
392 if (__tme_predict_false(arg_code == TME_LOG_ARG_CODE_STRING)) {
393
394 /* get the string argument: */
395 prf_value_s = va_arg(prf_args, const char *);
396 string_length = strlen(prf_value_s);
397
398 /* add the string argument to the message: */
399 assert ((message_size + string_length + 1) <= TME_LOG_MESSAGE_SIZE_MAX_BINARY);
400 memcpy(message + message_size,
401 prf_value_s,
402 string_length + 1);
403 message_size += string_length + 1;
404 }
405
406 /* otherwise, if this argument is a char: */
407 else if (arg_code == TME_LOG_ARG_CODE_CHAR) {
408
409 /* get the (promoted) int-sized integral argument: */
410 prf_value_d = va_arg(prf_args, int);
411
412 /* add the char-sized argument to the message: */
413 assert ((message_size + sizeof(char)) <= TME_LOG_MESSAGE_SIZE_MAX_BINARY);
414 *((char *) (message + message_size)) = prf_value_d;
415 message_size += sizeof(char);
416 }
417
418 /* otherwise, if this argument has the same size as an int: */
419 /* NB: this assumes that all integral types that have the same
420 size as an int are equivalent for va_arg(): */
421 else if (sizeof(int) != sizeof(long)
422 ? arg_code == TME_LOG_ARG_CODE_INT
423 : (prf_lld(arg_code != TME_LOG_ARG_CODE_LONG_LONG_INT &&) TRUE)) {
424
425 /* get the int-sized integral argument: */
426 prf_value_d = va_arg(prf_args, int);
427
428 /* add the int-sized argument to the message: */
429 assert ((message_size + sizeof(prf_value_d)) <= TME_LOG_MESSAGE_SIZE_MAX_BINARY);
430 if (_TME_LOG_ALIGN_ANY(prf_value_d)) {
431 *((int *) (message + message_size)) = prf_value_d;
432 }
433 else {
434 memcpy(message + message_size,
435 &prf_value_d,
436 sizeof(prf_value_d));
437 }
438 message_size += sizeof(prf_value_d);
439 }
440
441 /* otherwise, if this argument has the same size as a long int: */
442 /* NB: this assumes that all integral types that have the same
443 size as a long int are equivalent for va_arg(): */
444 else if (sizeof(int) != sizeof(long)
445 prf_lld(&& (((sizeof(int) * 2) == sizeof(long))
446 || arg_code == TME_LOG_ARG_CODE_LONG_INT))) {
447
448 /* get the long-int-sized integral argument: */
449 prf_value_ld = va_arg(prf_args, long int);
450
451 /* add the long-int-sized argument to the message: */
452 assert ((message_size + sizeof(prf_value_ld)) <= TME_LOG_MESSAGE_SIZE_MAX_BINARY);
453 if (_TME_LOG_ALIGN_ANY(prf_value_ld)) {
454 *((long int *) (message + message_size)) = prf_value_ld;
455 }
456 else {
457 memcpy(message + message_size,
458 &prf_value_ld,
459 sizeof(prf_value_ld));
460 }
461 message_size += sizeof(prf_value_ld);
462 }
463
464 /* otherwise, this argument must have the same size as a long
465 long int: */
466 /* NB: this assumes that all integral types that have the same
467 size as a long int are equivalent for va_arg(): */
468 else {
469 assert (arg_code == TME_LOG_ARG_CODE_LONG_LONG_INT);
470
471 #if (0 prf_lld(+ 1))
472
473 /* get the long-long-int-sized integral argument: */
474 prf_value_lld = va_arg(prf_args, long long int);
475
476 /* add the long-long-int-sized argument to the message: */
477 assert ((message_size + sizeof(prf_value_lld)) <= TME_LOG_MESSAGE_SIZE_MAX_BINARY);
478 if (_TME_LOG_ALIGN_ANY(prf_value_lld)) {
479 *((long long int *) (message + message_size)) = prf_value_lld;
480 }
481 else {
482 memcpy(message + message_size,
483 &prf_value_lld,
484 sizeof(prf_value_lld));
485 }
486 message_size += sizeof(prf_value_lld);
487
488 #else /* long long int not supported */
489 abort();
490 #endif /* long long int not supported */
491 }
492 }
493
494 /* end the variable arguments: */
495 va_end(prf_args);
496
497 /* update the current message: */
498 handle->tme_log_handle_message_size = message_size;
499
500 return;
501 }
502
503 #define PRF_OUT_ARG_CODE(arg_code) do { } while (/* CONSTCOND */ 0 && (arg_code))
504 #define PRF_OUT_MEM(s, len) tme_output_append_raw(&handle->tme_log_handle_message, s, len)
505 #define PRF_OUT_CHAR(c) tme_output_xpend_char(&handle->tme_log_handle_message, c, FALSE)
506
507 saved_errno = errno;
508 do
509 #include "log-prf.c"
510 while (/* CONSTCOND */ 0);
511 errno = saved_errno;
512
513 #undef PRF_OUT_ARG_CODE
514 #undef PRF_OUT_MEM
515 #undef PRF_OUT_CHAR
516 }
517