Merge branch 'master' of git://git.openpandora.org/pandora-libraries
[pandora-libraries.git] / lib / pnd_logger.c
1
2 #include <stdarg.h> // va-args
3 #include <stdlib.h> // malloc/free
4 #include <string.h> // strdup
5 #include "pnd_logger.h"
6
7 char *log_pretext = NULL;
8 unsigned char log_filterlevel = 0;
9
10 typedef enum {
11   pndl_nil = 0,
12   pndl_stream,
13   pndl_syslog,
14   pndl_callback
15 } pnd_logger_e;
16
17 typedef struct {
18   pnd_logger_e type;
19   union {
20     FILE *stream;
21     pnd_log_callback_f callback;
22   };
23 } pnd_log_target_t;
24
25 #define PND_LOG_MAX 5
26 static pnd_log_target_t log_targets [ PND_LOG_MAX ]; // implicitly nil
27
28 static int pnd_log_empty_slot ( void ) {
29   unsigned char i;
30
31   for ( i = 0; i < PND_LOG_MAX; i++ ) {
32     if ( log_targets [ i ].type == pndl_nil ) {
33       return ( i );
34     }
35   }
36
37   return ( -1 );
38 }
39
40 unsigned char pnd_log_set_filter ( unsigned char newlevel ) {
41   unsigned char foo = log_filterlevel;
42   log_filterlevel = newlevel;
43   return ( foo );
44 }
45
46 unsigned char pnd_log_get_filter ( void ) {
47   return ( log_filterlevel );
48 }
49
50 void pnd_log_set_pretext ( char *pre ) {
51
52   if ( log_pretext ) {
53     free ( log_pretext );
54     log_pretext = NULL;
55   }
56
57   if ( pre ) {
58     log_pretext = strdup ( pre );
59   }
60
61   return;
62 }
63
64 void pnd_log_to_nil ( void ) {
65   memset ( log_targets, '\0', sizeof(pnd_log_target_t) * PND_LOG_MAX );
66   return;
67 }
68
69 unsigned char pnd_log_to_stream ( FILE *f ) {
70   int i = pnd_log_empty_slot();
71
72   if ( i < 0 ) {
73     return ( 0 ); // fail!
74   }
75
76   log_targets [ i ].type = pndl_stream;
77   log_targets [ i ].stream = f;
78
79   return ( 1 );
80 }
81
82 unsigned char pnd_log_to_syslog ( char *facility ) {
83   return ( 0 ); // NYI
84 }
85
86 unsigned char pnd_log_to_callback ( pnd_log_callback_f f, void *userdata ) {
87   return ( 0 ); // NYI
88 }
89
90 unsigned char pnd_log_to_stdout ( void ) {
91   return ( pnd_log_to_stream ( stdout ) );
92 }
93
94 unsigned char pnd_log_to_stderr ( void ) {
95   return ( pnd_log_to_stream ( stderr ) );
96 }
97
98 unsigned char pnd_log_max_targets ( void ) {
99   return ( PND_LOG_MAX );
100 }
101
102 void pnd_log_emit ( char *message ) {
103   unsigned char i;
104
105   // iterate across targets and attempt to emit
106   for ( i = 0; i < PND_LOG_MAX; i++ ) {
107
108     switch ( log_targets [ i ].type ) {
109
110     case pndl_nil:
111       // nop
112       break;
113
114     case pndl_stream:
115       if ( log_pretext ) {
116         fprintf ( log_targets [ i ].stream, "%s\t", log_pretext );
117       }
118       if ( message ) {
119         fprintf ( log_targets [ i ].stream, "%s", message );
120         if ( strchr ( message, '\n' ) == NULL ) {
121           fprintf ( log_targets [ i ].stream, "\n" );
122         }
123       }
124       break;
125
126     case pndl_syslog:
127       // NYI
128       break;
129
130     case pndl_callback:
131       // NYI
132       break;
133
134     } // switch
135
136   } // for
137
138   return;
139 }
140
141 unsigned char pnd_log ( unsigned char level, char *fmt, ... ) {
142
143   if ( level == PND_LOG_FORCE ) {
144     // always proceed
145   } else if ( level < log_filterlevel ) {
146     return ( 0 ); // too low level
147   }
148
149   // format the actual log string
150   int n, size = 100;
151   char *p, *np;
152   va_list ap;
153
154   if ( ( p = malloc ( size ) ) == NULL ) {
155     return ( 0 ); // fail!
156   }
157
158   while  ( 1 ) {
159
160     /* Try to print in the allocated space. */
161     va_start ( ap, fmt );
162     n = vsnprintf ( p, size, fmt, ap );
163     va_end ( ap );
164
165     /* If that worked, return the string. */
166     if ( n > -1 && n < size ) {
167       pnd_log_emit ( p );
168       break;
169     }
170
171     /* Else try again with more space. */
172     if ( n > -1 )   /* glibc 2.1 */
173       size = n + 1; /* precisely what is needed */
174     else           /* glibc 2.0 */
175       size *= 2;  /* twice the old size */
176     if ( ( np = realloc ( p, size ) ) == NULL ) {
177       free(p);
178       return ( 0 ); // fail!
179     } else {
180       p = np;
181     }
182
183   } // while
184
185   if ( p ) {
186     free ( p );
187   }
188
189   return ( 1 );
190 }