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